class: center, middle, inverse, title-slide # Geospatial Techniques for Social Scientists in R ## Advanced Maps II ### Stefan Jünger & Anne-Kathrin Stroppe
February 09, 2021 --- layout: true --- ## Now <table class="table" style="margin-left: auto; margin-right: auto;"> <thead> <tr> <th style="text-align:left;"> Day </th> <th style="text-align:left;"> Time </th> <th style="text-align:left;"> Title </th> </tr> </thead> <tbody> <tr> <td style="text-align:left;color: gray !important;"> February 08 </td> <td style="text-align:left;color: gray !important;"> 09:00am-10:30am </td> <td style="text-align:left;font-weight: bold;"> Introduction </td> </tr> <tr> <td style="text-align:left;color: gray !important;"> February 08 </td> <td style="text-align:left;color: gray !important;"> 11:00am-12:30pm </td> <td style="text-align:left;font-weight: bold;"> Vector Data </td> </tr> <tr> <td style="text-align:left;color: gray !important;color: gray !important;"> February 08 </td> <td style="text-align:left;color: gray !important;color: gray !important;"> 12:30pm-01:30pm </td> <td style="text-align:left;font-weight: bold;color: gray !important;"> Lunch Break </td> </tr> <tr> <td style="text-align:left;color: gray !important;"> February 08 </td> <td style="text-align:left;color: gray !important;"> 01:30pm-03:00pm </td> <td style="text-align:left;font-weight: bold;"> Basic Maps </td> </tr> <tr> <td style="text-align:left;color: gray !important;border-bottom: 1px solid"> February 08 </td> <td style="text-align:left;color: gray !important;border-bottom: 1px solid"> 03:30pm-05:00pm </td> <td style="text-align:left;font-weight: bold;border-bottom: 1px solid"> Raster Data </td> </tr> <tr> <td style="text-align:left;color: gray !important;"> February 09 </td> <td style="text-align:left;color: gray !important;"> 09:00am-10:30am </td> <td style="text-align:left;font-weight: bold;"> Advanced Data Import </td> </tr> <tr> <td style="text-align:left;color: gray !important;"> February 09 </td> <td style="text-align:left;color: gray !important;"> 11:00am-12:30pm </td> <td style="text-align:left;font-weight: bold;"> Applied Data Wrangling </td> </tr> <tr> <td style="text-align:left;color: gray !important;color: gray !important;"> February 09 </td> <td style="text-align:left;color: gray !important;color: gray !important;"> 12:30pm-13:30pm </td> <td style="text-align:left;font-weight: bold;color: gray !important;"> Lunch Break </td> </tr> <tr> <td style="text-align:left;color: gray !important;"> February 09 </td> <td style="text-align:left;color: gray !important;"> 01:30pm-03:00pm </td> <td style="text-align:left;font-weight: bold;"> Advanced Maps I </td> </tr> <tr> <td style="text-align:left;color: gray !important;background-color: yellow !important;"> February 09 </td> <td style="text-align:left;color: gray !important;background-color: yellow !important;"> 03:30pm-05:00pm </td> <td style="text-align:left;font-weight: bold;background-color: yellow !important;"> Advanced Maps II </td> </tr> </tbody> </table> --- ## What We Aim to Learn in This Session <img src="data:image/png;base64,#./img/fig_3d_simple.png" width="9083" style="display: block; margin: auto;" /> --- ## How Are We Going to Do It? We will make use of - `sf`'s excellent interface to straightforward manipulate the `geometry` column - barebones `ggplot2` again - most notably, some mathematical voodoo for rotating the data, called an affine transformation --- ## A Laypersons’ Explanation of the Mathematical Background Creating a 3D map comprises two steps. 1. Shearing the geometries 2. Rotating the geometries --- ## Shearing Shearing or shear mapping is a method of geometric data transformation that 'distorts' the data while preserving the size of the area - with a signed distance in reference - to a fixed line or point -- .pull-left[ From this... <img src="data:image/png;base64,#2_4_Advanced_Maps_II_files/figure-html/square-1.png" width="70%" style="display: block; margin: auto;" /> ] -- .pull-right[ ...to this. <img src="data:image/png;base64,#2_4_Advanced_Maps_II_files/figure-html/square-sheared-1.png" width="100%" style="display: block; margin: auto;" /> ] --- ## Rotation In our example, rotation serves an aesthetic purpose: - the horizontal line should not be horizontal - you don't need that necessarily .pull-left[ From this... <img src="data:image/png;base64,#2_4_Advanced_Maps_II_files/figure-html/square-sheared-2-1.png" width="100%" style="display: block; margin: auto;" /> ] -- .pull-right[ ...to this. <img src="data:image/png;base64,#2_4_Advanced_Maps_II_files/figure-html/square-sheared-rotated-1.png" width="100%" style="display: block; margin: auto;" /> ] --- ## Formula So we not only multiply any point in our data with a shear matrix, which serves the shear transformation, but also a rotation matrix: `$${[x,y]} \times \underbrace{\begin{bmatrix}2 & 0 \\ 1.2 & 1 \end{bmatrix}}_\text{Shear Matrix} \times \underbrace{\begin{bmatrix} \cos(\frac{\pi}{20}) & \sin(\frac{\pi}{20}) \\ -\sin(\frac{\pi}{20}) & \cos(\frac{\pi}{20})\end{bmatrix}}_\text{Rotation Matrix} \underbrace{(+ \begin{bmatrix}x\_add & y\_add \end{bmatrix})}_\text{Optional Additions}$$` We can also add an `\(x\)` or `\(y\)` offset value to move the whole thing in two-dimensional space at the end of this operation. This step creates this layer optic, as we've seen in the beginning. --- ## R Implementation ```r rotate_data <- function(data, x_add = 0, y_add = 0) { shear_matrix <- function () { matrix(c(2, 1.2, 0, 1), 2, 2) } rotate_matrix <- function(x) { matrix(c(cos(x), sin(x), -sin(x), cos(x)), 2, 2) } data %>% dplyr::mutate( geometry = .$geometry * shear_matrix() * rotate_matrix(pi/20) + c(x_add, y_add) ) } ``` --- ## Let's Start With a Simple Square Using `sf`, we can create a basic rectangle more or less, just like in a drawing program. .pull-left[ ```r simple_square <- sf::st_point(1:2) %>% sf::st_sfc() %>% sf::st_sf() %>% sf::st_buffer( 10, endCapStyle = "SQUARE" ) ``` ] -- .pull-right[ ```r ggplot() + geom_sf(data = simple_square) ``` <img src="data:image/png;base64,#2_4_Advanced_Maps_II_files/figure-html/simple-square-map-1.png" style="display: block; margin: auto;" /> ] --- ## Application of Our Pre-Defined Function What is nice about the pre-defined function is that we can unleash it on our drawn rectangle. .pull-left[ ```r simple_square_rotated <- simple_square %>% rotate_data() ``` ] -- .pull-right[ ```r ggplot() + geom_sf(data = simple_square_rotated) ``` <img src="data:image/png;base64,#2_4_Advanced_Maps_II_files/figure-html/simple-square-rotated-map-1.png" style="display: block; margin: auto;" /> ] --- ## Adding Multiple Layers We can also recycle our rectangle and adding a `y` offset value. .pull-left[ ```r ggplot() + geom_sf( data = simple_square %>% rotate_data() ) + geom_sf( data = simple_square %>% rotate_data(y_add = 15) ) ``` ] -- .pull-right[ <img src="data:image/png;base64,#2_4_Advanced_Maps_II_files/figure-html/simple-square-rotated-multiple-layers-exec-1.png" style="display: block; margin: auto;" /> ] --- ## What's Next? You can do this rotation stuff with any geometry: - points - lines - polygons In the upcoming exercise, you will get used to applying the function by rotating multiple different geometries. But first, we are going to learn from one more geospatial technique as a prerequisite... --- ## Geocoding! Our geometries should be centered around a specific point in space. In principle, we can use an arbitrary point in space. However, wouldn't it be nice to start from a place or address your familiar with? You can use geocoding to retrieve the place's point in space: - conversion from an address to a geocoordinate - there are multiple providers of such a service - we can use OpenstreetMap! --- ## Geocoding the GESIS Cologne Address ```r gesis_cologne <- tmaptools::geocode_OSM( "Unter Sachsenhausen 6, 50667 Köln", as.sf = TRUE, geometry = "point" ) %>% sf::st_transform(3035) %>% dplyr::select(query, geometry = point) gesis_cologne ``` ``` ## Simple feature collection with 1 feature and 1 field ## geometry type: POINT ## dimension: XY ## bbox: xmin: 4106871 ymin: 3096821 xmax: 4106871 ymax: 3096821 ## projected CRS: ETRS89-extended / LAEA Europe ## query geometry ## 1 Unter Sachsenhausen 6, 50667 Köln POINT (4106871 3096821) ``` --- class:middle ## Exercise 2_4_1: Pre-Shaping Data [Exercise](https://stefanjuenger.github.io/gesis-workshop-geospatial-techniques-R/exercises/2_4_1_Pre-Shaping_Data_question.html) [Solution](https://stefanjuenger.github.io/gesis-workshop-geospatial-techniques-R/solutions/2_4_1_Pre-Shaping_Data_solution.html) --- ## Going Crazy: Adding Custom Elements Some additional packages for `ggplot2` already provide functionalities to add custom elements - scale bars - compasses However, they are often small images added to the plot, which makes rotating them a bit hard. **Thus, we draw them ourselves as additional `sf` geometries.** --- ## Basic Scale Bar: Prerequisites First, let's use our `bboxing()` function from the previous exercise: ```r gesis_bbox <- bboxing(gesis_cologne) ``` It's the frame we are orientating on for 'drawing'. Just like your canvas in MS paint... --- ## Basic Scale Bar: the Whole Thing Scale bars have a specific length. We define it here by restructuring our pre-defined frame. .pull-left[ ```r gesis_cologne_scalebar <- (gesis_bbox + c(1000, 0, 0, -1950)) %>% sf::st_as_sfc() %>% sf::st_sf() ``` ] -- .pull-right[ ```r plot(gesis_cologne_scalebar) ``` <img src="data:image/png;base64,#2_4_Advanced_Maps_II_files/figure-html/scalebar-frame-plot-1.png" style="display: block; margin: auto;" /> ] --- ## Basic Scale Bar: Sections Often scale bars include sections for measuring distances. That's something we can also draw and add to our scalebar. .pull-left[ ```r gesis_cologne_scalebar <- rbind( gesis_cologne_scalebar %>% {sf::st_bbox(.) + c(750, 0, 0, 0)} %>% sf::st_as_sfc() %>% sf::st_sf(), gesis_cologne_scalebar %>% {sf::st_bbox(.) + c(500, 0, -250, 0)} %>% sf::st_as_sfc() %>% sf::st_sf(), gesis_cologne_scalebar %>% {sf::st_bbox(.) + c(250, 0, -500, 0)} %>% sf::st_as_sfc() %>% sf::st_sf(), gesis_cologne_scalebar %>% {sf::st_bbox(.) + c(0, 0, -750, 0)} %>% sf::st_as_sfc() %>% sf::st_sf() ) ``` ] -- .pull-right[ ```r plot(gesis_cologne_scalebar) ``` <img src="data:image/png;base64,#2_4_Advanced_Maps_II_files/figure-html/scalebar-sections-plot-1.png" style="display: block; margin: auto;" /> ] --- ## Unrotated Map With Scalebar Let's have a look at our map, including our custom scalebar. .pull-left[ ```r ggplot() + geom_sf(data = gesis_roads) + geom_sf( data = gesis_cologne_scalebar, color = "black", fill = rep(c("black", "white"), 2) ) + ggsn::blank() ``` ] -- .pull-right[ <img src="data:image/png;base64,#2_4_Advanced_Maps_II_files/figure-html/unrotated-map-exec-1.png" style="display: block; margin: auto;" /> ] --- ## Rotated Map With a Scalebar And now, let's see how it performs when rotating the whole map. .pull-left[ .tinyisher[ ```r ggplot() + geom_sf( data = gesis_roads %>% rotate_data() ) + geom_sf( data = gesis_cologne_scalebar %>% rotate_data(x_add = -150, y_add = -100), color = "black", fill = rep(c("black", "white"), 2) ) + annotate("text", x = gesis_cologne_scalebar %>% rotate_data(x_add = -150, y_add = -100) %>% sf::st_bbox() %>% .$xmin, y = gesis_cologne_scalebar %>% rotate_data(x_add = -150, y_add = -100) %>% sf::st_bbox() %>% .$ymax - 150, label = "0 km", angle = -12, size = 3 ) + annotate("text", x = gesis_cologne_scalebar %>% rotate_data(x_add = -150, y_add = -100) %>% sf::st_bbox() %>% .$xmax - 200, y = gesis_cologne_scalebar %>% rotate_data(x_add = -150, y_add = -100) %>% sf::st_bbox() %>% .$ymin - 130, label = "1 km", angle = -12, size = 3 ) + ggsn::blank() ``` ] ] -- .pull-right[ <img src="data:image/png;base64,#2_4_Advanced_Maps_II_files/figure-html/rotated-map-exec-1.png" style="display: block; margin: auto;" /> ] --- class:middle ## Exercise 2_4_2: Drawing Custom Elements [Exercise](https://stefanjuenger.github.io/gesis-workshop-geospatial-techniques-R/exercises/2_4_2_Drawing_Custom_Elements_question.html) [Solution](https://stefanjuenger.github.io/gesis-workshop-geospatial-techniques-R/solutions/2_4_2_Drawing_Custom_Elements_solution.html) --- class: middle ## Wrap-up --- layout: false class: center background-image: url(data:image/png;base64,#./assets/img/the_end.png) background-size: cover .left-column[ </br> <img src="data:image/png;base64,#./img/stefan.png" width="90%" style="display: block; margin: auto;" /> ] .right-column[ .left[.small[<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg" style="height:1em;fill:currentColor;position:relative;display:inline-block;top:.1em;"> [ comment ] <path d="M464 64H48C21.49 64 0 85.49 0 112v288c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V112c0-26.51-21.49-48-48-48zm0 48v40.805c-22.422 18.259-58.168 46.651-134.587 106.49-16.841 13.247-50.201 45.072-73.413 44.701-23.208.375-56.579-31.459-73.413-44.701C106.18 199.465 70.425 171.067 48 152.805V112h416zM48 400V214.398c22.914 18.251 55.409 43.862 104.938 82.646 21.857 17.205 60.134 55.186 103.062 54.955 42.717.231 80.509-37.199 103.053-54.947 49.528-38.783 82.032-64.401 104.947-82.653V400H48z"></path></svg> [`stefan.juenger@gesis.org`](mailto:stefan.juenger@gesis.org)] </br> .small[<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg" style="height:1em;fill:currentColor;position:relative;display:inline-block;top:.1em;"> [ comment ] <path d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"></path></svg> [`@StefanJuenger`](https://twitter.com/StefanJuenger)] </br> .small[<svg viewBox="0 0 496 512" xmlns="http://www.w3.org/2000/svg" style="height:1em;fill:currentColor;position:relative;display:inline-block;top:.1em;"> [ comment ] <path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"></path></svg> [`StefanJuenger`](https://github.com/StefanJuenger)] </br> .small[<svg viewBox="0 0 576 512" xmlns="http://www.w3.org/2000/svg" style="height:1em;fill:currentColor;position:relative;display:inline-block;top:.1em;"> [ comment ] <path d="M280.37 148.26L96 300.11V464a16 16 0 0 0 16 16l112.06-.29a16 16 0 0 0 15.92-16V368a16 16 0 0 1 16-16h64a16 16 0 0 1 16 16v95.64a16 16 0 0 0 16 16.05L464 480a16 16 0 0 0 16-16V300L295.67 148.26a12.19 12.19 0 0 0-15.3 0zM571.6 251.47L488 182.56V44.05a12 12 0 0 0-12-12h-56a12 12 0 0 0-12 12v72.61L318.47 43a48 48 0 0 0-61 0L4.34 251.47a12 12 0 0 0-1.6 16.9l25.5 31A12 12 0 0 0 45.15 301l235.22-193.74a12.19 12.19 0 0 1 15.3 0L530.9 301a12 12 0 0 0 16.9-1.6l25.5-31a12 12 0 0 0-1.7-16.93z"></path></svg> [`https://stefanjuenger.github.io`](https://stefanjuenger.github.io)]] </br> ]