class: left, bottom background-image: url("images/contour.png") background-position: right background-size: auto # Interactive Maps in R ### Feel the need to zoom and pan. <p> </p> <p> </p> <img src="images/logo1.svg" width="400px"> --- class: sectionTitle # A Brief `join` interlude. --- # Multi-Table Data Rarely do we keep our data within a single data table. .pull-left[ #### Table Structure Consider the two tables to the right. - Common column labeled *Key* - Other data columns (only 1 in each for brevity) ] .pull-right[![Example data table structure](https://live.staticflickr.com/65535/50427672632_24e45139a8_c_d.jpg)] --- # Varieties of Keys 🗝 .pull-left[### Primary Key A *primary key* is a column in a table that uniquely identifies a single row. There MUST be a unique identifier to be a *Primary Key*. ```r Key = c("A","B","C") Name = c("Bob","Alice","Mary") Major = c("ENVS", "ENVS", "BIOL") people <- data.frame( Key, Name, Major ) people %>% count( Key ) ``` ``` ## Key n ## 1 A 1 ## 2 B 1 ## 3 C 1 ``` ] -- .pull-right[ ### Foreign Key A foreign key is one that references a primary key in *another* table. ```r Key = c("A","B","A","D","B") Grade = rpois(5,lambda = 100) homework <- data.frame( Key, Grade ) homework ``` ``` ## Key Grade ## 1 A 112 ## 2 B 104 ## 3 A 104 ## 4 D 113 ## 5 B 105 ``` Here the `Key` column is referencing a unique row of data in the `people` data table. ] --- class: sectionTitle, inverse # .green[Joins] ### .fancy[Merging data tables] --- # Taxonomy of Joins We can combine the data in these tables in several different ways based upon what we are looking for. When thinking of joins, we must think about the how we want to select the overlapping sets of keys in both data.frames. - *Full Join* (aka *outer join*) - *Left Join* - *Right Join* - *Inner Join* The .redinline[*position*] adjective relates to which of the rows are selected for the join and end up in the resutling data table. --- # 'Left' & 'Right' Tables .center[ ![Example data table structure](https://live.staticflickr.com/65535/50427672632_24e45139a8_c_d.jpg) ] --- # Full/Outer Join The outer join has .redinline[all the data from both left & right tables]. All keys are present in the result. ![Outer Join](https://live.staticflickr.com/65535/50427993992_4ccede1574_c_d.jpg) --- # Outer Join All homework and people data. ```r people %>% full_join( homework, by="Key" ) ``` ``` ## Key Name Major Grade ## 1 A Bob ENVS 112 ## 2 A Bob ENVS 104 ## 3 B Alice ENVS 104 ## 4 B Alice ENVS 105 ## 5 C Mary BIOL NA ## 6 D <NA> <NA> 113 ``` --- # Left Join The left join is one where the result has all the keys from the left but only those in the right one that are in the left. ![left join](https://live.staticflickr.com/65535/50427817371_678f0f64c7_c_d.jpg) -- ```r people %>% left_join( homework, by="Key") ``` ``` ## Key Name Major Grade ## 1 A Bob ENVS 112 ## 2 A Bob ENVS 104 ## 3 B Alice ENVS 104 ## 4 B Alice ENVS 105 ## 5 C Mary BIOL NA ``` --- # Right Join The Right join results in all the keys from the right data table and the matching ones from the left. .center[ ![Right Join](https://live.staticflickr.com/65535/50427125528_0de6281475_c_d.jpg) ] -- ```r people %>% right_join( homework, by="Key") ``` ``` ## Key Name Major Grade ## 1 A Bob ENVS 112 ## 2 A Bob ENVS 104 ## 3 B Alice ENVS 104 ## 4 B Alice ENVS 105 ## 5 D <NA> <NA> 113 ``` --- # Inner Joins Inner joins result in the intersection of keys. ![Inner Join](https://live.staticflickr.com/65535/50427125683_ac44eb1500_c_d.jpg) -- ```r people %>% inner_join( homework, by="Key") ``` ``` ## Key Name Major Grade ## 1 A Bob ENVS 112 ## 2 A Bob ENVS 104 ## 3 B Alice ENVS 104 ## 4 B Alice ENVS 105 ``` --- class: sectionTitle, inverse # .blue[Filtering Joins] ## .fancy[Not combining but refining...] --- # The Semi Join We can also use joins to filter values within one `data.frame`. Here the `semi_join()` keeps everything in the left data that has a key in the right one, but **importantly** it does not import the right data columns into the result. -- ```r people %>% semi_join( homework, by="Key") ``` ``` ## Key Name Major ## 1 A Bob ENVS ## 2 B Alice ENVS ``` --- # The Anti Join The opposite of the `seim_join()` is the `anti_join()` which drops everything in the left table that has a key in the right one, leaving only the ones that are unique. -- ```r people %>% anti_join( homework, by = "Key") ``` ``` ## Key Name Major ## 1 C Mary BIOL ``` --- # Activity - Joining Field & GIS Data Create a new script file named `joins.R` and join average station measurements onto the `GIS_Data.csv` data set. --- class: sectionTitle # Leaflet ![](https://media.giphy.com/media/xznyPebL28X5u/giphy.gif) --- # Leaflet Library The Leaflet library is based upon javascript and is implemented in R for use in HTML output. It is essentially a Google Maps/Apple Maps interface that can be used to pipe data from R into with all the expected interactions. For this example, I'm going to use some data from my own research and let you use the `GIS_Data.csv` in the activity. --- .center[ ![](https://live.staticflickr.com/65535/51043197628_2d1f0578db_o_d.png)] .pull-left[ ### Provides: - Uniform interface - Many entry points (R, JavaScript, Python, HTML, etc.) - Variety of Map Types - Completely User Interactive ] .pull-right[ ![](https://live.staticflickr.com/65535/51044028622_5fa998274c_z_d.jpg) ] --- # Map Making Workflow I'd make flow diagram here to go through basic steps. Define what each does in the background. 1. Get data - 2. Make base map - `leaflet()` function creates widget for map and all javascript 3. Define Region - `setVeiw()` can be used to define the `bbox()` 4. Add Tiles - `addTiles()` or `addProviderTiles()` downloads map tiles 5. Overlay Markers - `addMarkers()` make your points 6. Overlay Rasters - 7. Set Fancy Stuff - -- 8. There is no step 8 🎉 !!!!! --- class: sectionTitle, inverse # .green[Basic Map Parts] ### .fancy[The foundation parts
] --- # The Base Map .pull-left[ Just like `ggplot()`, calling `leaflet()` just creates a viewport into which we need to add information. ```r leaflet() ``` ] .pull-right[
] --- # Adding Default Tiles .pull-left[ Tiles are essentially static images of a specified geographic size. As you "zoom" in (+ & - buttons) a new set of images are fetched from a tile provider for that scale. Here we can add default tiles (these come from OpenStreetMap). ```r leaflet() %>% addTiles() ``` ] .pull-right[
] --- # Setting a Boundary ViewPort .pull-left[ We can specify a particular bounding box for our viewport to focus on. This is based upon a point to be taken as a centroid as well as a pre-defined zoom level. ```r leaflet() %>% addTiles() %>% setView(lng = -77.5, lat = 37.5, zoom = 10) ``` ] .pull-right[
] --- # Tile Providers There are some really interesting Tile Providers that we can use by replacing ```r addTiles() ``` with ```r addProviderTiles( providers$Name ) ``` where `Name` can be any of the items found [here](http://leaflet-extras.github.io/leaflet-providers/preview/index.html) .red[<-- click on this] --- # Example Provider Tiles - OpenTopoMap
--- # Example Provider Tiles - ESRI World Topo
--- # Example Provider Tiles - Watercolor
--- # Example Provider Tiles - ESRI Satelite
--- # Example Provider Tiles - ESRI World Shaded Relief
--- # Example Provider Tiles - Earth At Night
--- # Example Provider Tiles - NASA Daily Composite Imagry
--- .pull-left[ # Combining Tiles Tile providers also make available various labels to put on the map. You can get access to these as another provider layer and overlay them ontop of the map. ```r provider <- providers$Stamen.Watercolor labels <- providers$CartoDB.VoyagerOnlyLabels leaflet() %>% setView(lng = -77.5, lat = 37.5, zoom = 6) %>% addProviderTiles(provider) %>% addProviderTiles(labels) ``` ] .pull-right[
] --- class: sectionTitle, inverse # .green[Adding Data] ### .fancy[Increasing Information Content] --- # The Data To add components to the leaflet map, you need to get the data up and ready. For this, we'll go back to the enigmatic Bark Beetle data set. ```r library( sf ) library( tidyverse ) url <- "https://raw.githubusercontent.com/dyerlab/ENVS-Lectures/master/data/Araptus_Disperal_Bias.csv" read.csv(url) %>% st_as_sf( coords=c("Longitude","Latitude"), crs=4326 ) -> beetles head( beetles, n=3 ) ``` ``` ## Simple feature collection with 3 features and 7 fields ## Geometry type: POINT ## Dimension: XY ## Bounding box: xmin: -112.0461 ymin: 24.00789 xmax: -109.327 ymax: 26.94589 ## Geodetic CRS: WGS 84 ## Site Males Females Suitability MFRatio GenVarArapat GenVarEuphli ## 1 32 40 27 0.0563 1.4815 0.1436067 0.2185199 ## 2 73 11 5 0.1455 2.2000 0.1373044 0.2534679 ## 3 93 25 21 0.1627 1.1905 0.1630808 0.1325203 ## geometry ## 1 POINT (-109.327 26.63783) ## 2 POINT (-109.8507 24.00789) ## 3 POINT (-112.0461 26.94589) ``` --- # Data for `leaflet` Both `leaflet()` and each map layer have a data = parameter. Spatial data can be in the form of: - `dataframe` with lat/lng columns - `sp` objects - `sf` objects - `maps` package `map()` objects Data can be passed through the `leaflet()` function or through the map layers. --- .pull-left[ # Shapes (Circles, Lines & Polygons) Leaflet will take the extent of the points and try to find a proper zoom and extent so that each point is shown. ```r leaflet() %>% addTiles() %>% addCircles( data=beetles ) ``` Note: Circles scale with the map ] .pull-right[
] --- .pull-left[ # Map Circles - size We can easily associate features on the map based upon data being passed in. Here I take the count of male beetles from each site (and scale it for visual appeal) to show spatail variation in demograhic parameters. ```r leaflet(beetles) %>% addTiles() %>% addCircles( radius = ~Males *500, stroke = FALSE, fillOpacity = .5) ``` ] .pull-right[
] --- .pull-left[ # Map Color You can specify the color palette for the plots. Here we use one going from Red to Blu along a numeric gradient and use the Male:Female sex ratio of the site interpret the fill color. ```r pal <- colorNumeric( palette = "RdBu", domain = beetles$MFRatio, reverse = TRUE ) leaflet(beetles) %>% addTiles() %>% addCircles(color = ~pal(MFRatio), fillOpacity = .7, radius = 15000, stroke = FALSE) ``` ] .pull-right[
] --- .pull-left[ # Legends All graphics are better if there is a legend to help guide the viewer in interpreting what they are looking at. ```r pal <- colorNumeric( palette = "RdBu", domain = beetles$MFRatio, reverse = TRUE) leaflet(beetles) %>% addProviderTiles(providers$CartoDB.Positron) %>% addCircles( fillColor = ~pal(MFRatio), fillOpacity = .7, radius = 15000, weight = 1, color = "grey") %>% addLegend(pal = pal, values = ~MFRatio, title = "Male to Female Ratio") ``` ] .pull-right[
] --- .pull-left[ # Map Markers Instead of shapes, we can use the enigmatic map marker to denote location. These are invariant of zoom level. ```r leaflet() %>% addTiles() %>% addMarkers( data=beetles ) ``` ] .pull-right[
] --- .pull-left[ # Annotations In addition to the symbology, we can define a wide range of content to be associated with each marker. Here is an example of a static annotation. ```r leaflet(beetles) %>% addProviderTiles(providers$CartoDB.Positron) %>% setView(lng = -111, lat = 25, zoom = 7) %>% addPopups(lng = -111, lat = 25, paste0("beetles live here")) ``` ] .pull-right[
] --- .pull-left[ # Pop-ups We can also designate a message to be shown when the user clicks on the site. Here I also add a custom icon to the marker from fontawesome. ```r makeAwesomeIcon(icon = "bug", library = "fa", markerColor = "lightgreen", iconColor = "blue") -> bug leaflet(beetle) %>% addProviderTiles(providers$CartoDB.Positron) %>% addAwesomeMarkers( icon = bug, popup = paste0( "Site:", beetles$Site)) ``` ] .pull-right[
] --- # Large-ish Activity - Interactive Map Make a new R file in the same folder as the project named `leaflet_examples.R`. In this file, 1. Load in the contents of the `GIS_Data.csv` using `read_csv()` from `tidyverse`. By now, you should be getting the handle of this, make sure to import the proper libraries, including `library(leaflet)`. Name the variable you assign these data `Stations`. 2. Create a basic map with from these data with each station shown as a plain marker. 3. Add labels defined by `Sta_Desc` to that map as a popup. 4. Left join the `GIS_Data.csv` and the `Field_Data.csv` to make a single data.frame that also includes the variance in depths for each station. Use this to make a map where the size of the icon is depicted by the variation in depth for each location. --- class: middle background-image: url("images/contour.png") background-position: right background-size: auto # Any Further Questions? .center[ ![## Any Questions](https://media.giphy.com/media/XK6DalN4YB9bZfokQY/giphy.gif) ]