From 8bcf2bfe0a265d1260884023146923001a1ad88c Mon Sep 17 00:00:00 2001 From: Nowosad Date: Sun, 19 May 2024 01:14:48 +0000 Subject: [PATCH] Deploy commit: 8c85dea9c95c615122d9d2bf8d9fad5a848db5a3 --- 02-spatial-data.md | 35 ++++++++-- 03-attribute-operations.md | 69 ++++++++++++++------ 04-spatial-operations.md | 61 ++++++++++++++---- 05-geometry-operations.md | 34 +++++++--- 06-raster-vector.md | 20 ++++-- 07-reproj.md | 31 +++++++-- 08-read-write-plot.md | 28 +++++--- 09-mapping.md | 56 +++++++++++----- 10-gis.md | 18 +++--- 11-algorithms.md | 8 +-- 12-spatial-cv.md | 14 ++-- 13-transport.md | 4 +- 14-location.md | 6 +- 15-eco.md | 8 +-- 404.html | 2 +- adv-map.html | 75 ++++++++++++---------- algorithms.html | 10 +-- attr.html | 93 +++++++++++++++------------ eco.html | 10 +-- geometric-operations.html | 48 ++++++++------ gis.html | 20 +++--- index.html | 2 +- index.md | 2 +- intro.html | 2 +- location.html | 8 +-- raster-vector.html | 26 ++++---- read-write.html | 38 ++++++----- reproj-geo-data.html | 47 ++++++++------ search.json | 2 +- spatial-class.html | 47 ++++++++------ spatial-cv.html | 16 ++--- spatial-operations.html | 127 ++++++++++++++++++++----------------- transport.html | 6 +- 33 files changed, 598 insertions(+), 375 deletions(-) diff --git a/02-spatial-data.md b/02-spatial-data.md index 2084bd4..4402246 100644 --- a/02-spatial-data.md +++ b/02-spatial-data.md @@ -5,7 +5,7 @@ -```r +``` r library(sf) library(spData) library(terra) @@ -18,7 +18,7 @@ E1. Use `summary()` on the geometry column of the `world` data object that is in - Its coordinate reference system (CRS)? -```r +``` r summary(world) #> iso_a2 name_long continent region_un #> Length:177 Length:177 Length:177 Length:177 @@ -44,6 +44,9 @@ summary(world) #> 3rd Qu.:76.8 3rd Qu.: 24233 #> Max. :83.6 Max. :120860 #> NA's :10 NA's :17 +``` + +``` r # - Its geometry type? # multipolygon # - The number of countries? @@ -59,11 +62,14 @@ Find two similarities and two differences between the image on your computer and - Why was `cex` set to the `sqrt(world$pop) / 10000`? - Bonus: experiment with different ways to visualize the global population. -```r +``` r plot(world["continent"], reset = FALSE) cex = sqrt(world$pop) / 10000 world_cents = st_centroid(world, of_largest = TRUE) #> Warning: st_centroid assumes attributes are constant over geometries +``` + +``` r plot(st_geometry(world_cents), add = TRUE, cex = cex) # - What does the `cex` argument do (see `?plot`)? # It specifies the size of the circles @@ -92,7 +98,7 @@ E3. Use `plot()` to create maps of Nigeria in context (see Section 2.2.3). - Adjust the `lwd`, `col` and `expandBB` arguments of `plot()`. - Challenge: read the documentation of `text()` and annotate the map. -```r +``` r nigeria = world[world$name_long == "Nigeria", ] plot(st_geometry(nigeria), expandBB = c(0, 0.2, 0.1, 1), col = "gray", lwd = 3) plot(world[0], add = TRUE) @@ -108,6 +114,9 @@ plot(st_geometry(nigeria), col = "yellow", add = TRUE, border = "darkgrey") a = africa[grepl("Niger", africa$name_long), ] ncentre = st_centroid(a) #> Warning: st_centroid assumes attributes are constant over geometries +``` + +``` r ncentre_num = st_coordinates(ncentre) text(x = ncentre_num[, 1], y = ncentre_num[, 2], labels = a$name_long) ``` @@ -117,7 +126,7 @@ text(x = ncentre_num[, 1], y = ncentre_num[, 2], labels = a$name_long) E4. Create an empty `SpatRaster` object called `my_raster` with 10 columns and 10 rows. Assign random values between 0 and 10 to the new raster and plot it. -```r +``` r my_raster = rast(ncol = 10, nrow = 10, vals = sample(0:10, size = 10 * 10, replace = TRUE)) plot(my_raster) @@ -128,16 +137,28 @@ plot(my_raster) E5. Read-in the `raster/nlcd.tif` file from the **spDataLarge** package. What kind of information can you get about the properties of this file? -```r +``` r nlcd = rast(system.file("raster/nlcd.tif", package = "spDataLarge")) dim(nlcd) # dimensions #> [1] 1359 1073 1 +``` + +``` r res(nlcd) # resolution #> [1] 31.5 31.5 +``` + +``` r ext(nlcd) # extent #> SpatExtent : 301903.344386758, 335735.354381954, 4111244.46098842, 4154086.47216415 (xmin, xmax, ymin, ymax) +``` + +``` r nlyr(nlcd) # number of layers #> [1] 1 +``` + +``` r cat(crs(nlcd)) # CRS #> PROJCRS["NAD83 / UTM zone 12N", #> BASEGEOGCRS["NAD83", @@ -182,7 +203,7 @@ cat(crs(nlcd)) # CRS E6. Check the CRS of the `raster/nlcd.tif` file from the **spDataLarge** package. What kind of information you can learn from it? -```r +``` r cat(crs(nlcd)) #> PROJCRS["NAD83 / UTM zone 12N", #> BASEGEOGCRS["NAD83", diff --git a/03-attribute-operations.md b/03-attribute-operations.md index 0e1d8ba..4f101af 100644 --- a/03-attribute-operations.md +++ b/03-attribute-operations.md @@ -6,7 +6,7 @@ For these exercises we will use the `us_states` and `us_states_df` datasets from the **spData** package. You must have attached the package, and other packages used in the attribute operations chapter (**sf**, **dplyr**, **terra**) with commands such as `library(spData)` before attempting these exercises: -```r +``` r library(sf) library(dplyr) library(terra) @@ -22,10 +22,13 @@ The data comes from the United States Census Bureau, and is documented in `?us_s E1. Create a new object called `us_states_name` that contains only the `NAME` column from the `us_states` object using either base R (`[`) or tidyverse (`select()`) syntax. What is the class of the new object and what makes it geographic? -```r +``` r us_states_name = us_states["NAME"] class(us_states_name) #> [1] "sf" "data.frame" +``` + +``` r attributes(us_states_name) #> $names #> [1] "NAME" "geometry" @@ -44,6 +47,9 @@ attributes(us_states_name) #> NAME #> #> Levels: constant aggregate identity +``` + +``` r attributes(us_states_name$geometry) #> $n_empty #> [1] 0 @@ -83,7 +89,7 @@ E2. Select columns from the `us_states` object which contain population data. Obtain the same result using a different command (bonus: try to find three ways of obtaining the same result). Hint: try to use helper functions, such as `contains` or `matches` from **dplyr** (see `?contains`). -```r +``` r us_states |> select(total_pop_10, total_pop_15) #> Simple feature collection with 49 features and 2 fields #> Geometry type: MULTIPOLYGON @@ -102,6 +108,9 @@ us_states |> select(total_pop_10, total_pop_15) #> 8 6417398 6568645 MULTIPOLYGON (((-87.5 41.7,... #> 9 2809329 2892987 MULTIPOLYGON (((-102 40, -1... #> 10 4429940 4625253 MULTIPOLYGON (((-92 29.6, -... +``` + +``` r # or us_states |> select(starts_with("total_pop")) @@ -122,6 +131,9 @@ us_states |> select(starts_with("total_pop")) #> 8 6417398 6568645 MULTIPOLYGON (((-87.5 41.7,... #> 9 2809329 2892987 MULTIPOLYGON (((-102 40, -1... #> 10 4429940 4625253 MULTIPOLYGON (((-92 29.6, -... +``` + +``` r # or us_states |> select(contains("total_pop")) @@ -142,6 +154,9 @@ us_states |> select(contains("total_pop")) #> 8 6417398 6568645 MULTIPOLYGON (((-87.5 41.7,... #> 9 2809329 2892987 MULTIPOLYGON (((-102 40, -1... #> 10 4429940 4625253 MULTIPOLYGON (((-92 29.6, -... +``` + +``` r # or us_states |> select(matches("tal_p")) @@ -170,7 +185,7 @@ E3. Find all states with the following characteristics (bonus find *and* plot th - Belong to the West region, have an area below 250,000 km^2^ *and* in 2015 a population greater than 5,000,000 residents (hint: you may need to use the function `units::set_units()` or `as.numeric()`). - Belong to the South region, had an area larger than 150,000 km^2^ and a total population in 2015 larger than 7,000,000 residents. -```r +``` r us_states |> filter(REGION == "Midwest") #> Simple feature collection with 12 features and 6 fields @@ -201,6 +216,9 @@ us_states |> #> 8 MULTIPOLYGON (((-96.5 43.5,... #> 9 MULTIPOLYGON (((-85.6 45.6,... #> 10 MULTIPOLYGON (((-104 43, -1... +``` + +``` r us_states |> filter(REGION == "West", AREA < units::set_units(250000, km^2), total_pop_15 > 5000000) #> Simple feature collection with 1 feature and 6 fields @@ -212,6 +230,9 @@ us_states |> filter(REGION == "West", AREA < units::set_units(250000, km^2), tot #> 1 53 Washington West 175436 [km^2] 6561297 6985464 #> geometry #> 1 MULTIPOLYGON (((-123 48.2, ... +``` + +``` r # or us_states |> filter(REGION == "West", as.numeric(AREA) < 250000, total_pop_15 > 5000000) #> Simple feature collection with 1 feature and 6 fields @@ -223,6 +244,9 @@ us_states |> filter(REGION == "West", as.numeric(AREA) < 250000, total_pop_15 > #> 1 53 Washington West 175436 [km^2] 6561297 6985464 #> geometry #> 1 MULTIPOLYGON (((-123 48.2, ... +``` + +``` r us_states |> filter(REGION == "South", AREA > units::set_units(150000, km^2), total_pop_15 > 7000000) #> Simple feature collection with 3 features and 6 fields @@ -238,6 +262,9 @@ us_states |> filter(REGION == "South", AREA > units::set_units(150000, km^2), to #> 1 MULTIPOLYGON (((-81.8 24.6,... #> 2 MULTIPOLYGON (((-85.6 35, -... #> 3 MULTIPOLYGON (((-103 36.5, ... +``` + +``` r # or us_states |> filter(REGION == "South", as.numeric(AREA) > 150000, total_pop_15 > 7000000) #> Simple feature collection with 3 features and 6 fields @@ -258,7 +285,7 @@ us_states |> filter(REGION == "South", as.numeric(AREA) > 150000, total_pop_15 > E4. What was the total population in 2015 in the `us_states` dataset? What was the minimum and maximum total population in 2015? -```r +``` r us_states |> summarize(total_pop = sum(total_pop_15), min_pop = min(total_pop_15), max_pop = max(total_pop_15)) @@ -273,7 +300,7 @@ us_states |> summarize(total_pop = sum(total_pop_15), E5. How many states are there in each region? -```r +``` r us_states |> group_by(REGION) |> summarize(nr_of_states = n()) @@ -294,7 +321,7 @@ us_states |> E6. What was the minimum and maximum total population in 2015 in each region? What was the total population in 2015 in each region? -```r +``` r us_states |> group_by(REGION) |> summarize(min_pop = min(total_pop_15), @@ -319,7 +346,7 @@ What function did you use and why? Which variable is the key in both datasets? What is the class of the new object? -```r +``` r us_states_stats = us_states |> left_join(us_states_df, by = c("NAME" = "state")) class(us_states_stats) @@ -329,7 +356,7 @@ class(us_states_stats) E8. `us_states_df` has two more rows than `us_states`. How can you find them? (hint: try to use the `dplyr::anti_join()` function) -```r +``` r us_states_df |> anti_join(st_drop_geometry(us_states), by = c("state" = "NAME")) #> # A tibble: 2 × 5 @@ -342,7 +369,7 @@ us_states_df |> E9. What was the population density in 2015 in each state? What was the population density in 2010 in each state? -```r +``` r us_states2 = us_states |> mutate(pop_dens_15 = total_pop_15/AREA, pop_dens_10 = total_pop_10/AREA) @@ -351,7 +378,7 @@ us_states2 = us_states |> E10. How much has population density changed between 2010 and 2015 in each state? Calculate the change in percentages and map them. -```r +``` r us_popdens_change = us_states2 |> mutate(pop_dens_diff_10_15 = pop_dens_15 - pop_dens_10, pop_dens_diff_10_15p = (pop_dens_diff_10_15/pop_dens_10) * 100) @@ -362,7 +389,7 @@ plot(us_popdens_change["pop_dens_diff_10_15p"]) E11. Change the columns' names in `us_states` to lowercase. (Hint: helper functions - `tolower()` and `colnames()` may help.) -```r +``` r us_states %>% setNames(tolower(colnames(.))) #> Simple feature collection with 49 features and 6 fields @@ -399,7 +426,7 @@ E12. Using `us_states` and `us_states_df` create a new object called `us_states_ The new object should have only two variables - `median_income_15` and `geometry`. Change the name of the `median_income_15` column to `Income`. -```r +``` r us_states_sel = us_states |> left_join(us_states_df, by = c("NAME" = "state")) |> select(Income = median_income_15) @@ -408,7 +435,7 @@ us_states_sel = us_states |> E13. Calculate the change in the number of residents living below the poverty level between 2010 and 2015 for each state. (Hint: See ?us_states_df for documentation on the poverty level columns.) Bonus: Calculate the change in the *percentage* of residents living below the poverty level in each state. -```r +``` r us_pov_change = us_states |> left_join(us_states_df, by = c("NAME" = "state")) |> mutate(pov_change = poverty_level_15 - poverty_level_10) @@ -424,7 +451,7 @@ us_pov_pct_change = us_states |> E14. What was the minimum, average and maximum state's number of people living below the poverty line in 2015 for each region? Bonus: What is the region with the largest increase in people living below the poverty line? -```r +``` r us_pov_change_reg = us_pov_change |> group_by(REGION) |> summarize(min_state_pov_15 = min(poverty_level_15), @@ -445,7 +472,7 @@ E15. Create a raster from scratch with nine rows and columns and a resolution of Fill it with random numbers. Extract the values of the four corner cells. -```r +``` r r = rast(nrow = 9, ncol = 9, res = 0.5, xmin = 0, xmax = 4.5, ymin = 0, ymax = 4.5, vals = rnorm(81)) @@ -456,6 +483,9 @@ r[c(1, 9, 81 - 9 + 1, 81)] #> 2 -0.265 #> 3 -0.587 #> 4 -2.593 +``` + +``` r r[c(1, nrow(r)), c(1, ncol(r))] #> lyr.1 #> 1 1.434 @@ -466,7 +496,7 @@ r[c(1, nrow(r)), c(1, ncol(r))] E16. What is the most common class of our example raster `grain`? -```r +``` r grain = rast(system.file("raster/grain.tif", package = "spData")) freq(grain) |> arrange(-count )# the most common classes are silt and sand (13 cells) @@ -478,7 +508,7 @@ freq(grain) |> E17. Plot the histogram and the boxplot of the `dem.tif` file from the **spDataLarge** package (`system.file("raster/dem.tif", package = "spDataLarge")`). -```r +``` r dem = rast(system.file("raster/dem.tif", package = "spDataLarge")) hist(dem) boxplot(dem) @@ -487,6 +517,9 @@ boxplot(dem) library(ggplot2) ggplot(as.data.frame(dem), aes(dem)) + geom_histogram() #> `stat_bin()` using `bins = 30`. Pick better value with `binwidth`. +``` + +``` r ggplot(as.data.frame(dem), aes(dem)) + geom_boxplot() ``` diff --git a/04-spatial-operations.md b/04-spatial-operations.md index e19bddc..39cf105 100644 --- a/04-spatial-operations.md +++ b/04-spatial-operations.md @@ -3,7 +3,7 @@ -```r +``` r library(sf) library(dplyr) library(spData) @@ -14,12 +14,15 @@ How many of these high points does the Canterbury region contain? **Bonus:** plot the result using the `plot()` function to show all of New Zealand, `canterbury` region highlighted in yellow, high points in Canterbury represented by red crosses (hint: `pch = 7`) and high points in other parts of New Zealand represented by blue circles. See the help page `?points` for details with an illustration of different `pch` values. -```r +``` r canterbury = nz |> filter(Name == "Canterbury") canterbury_height = nz_height[canterbury, ] nz_not_canterbury_height = nz_height[canterbury, , op = st_disjoint] nrow(canterbury_height) # answer: 70 #> [1] 70 +``` + +``` r plot(st_geometry(nz)) plot(st_geometry(canterbury), col = "yellow", add = TRUE) @@ -31,7 +34,7 @@ plot(canterbury_height$geometry, pch = 4, col = "red", add = TRUE) E2. Which region has the second highest number of `nz_height` points, and how many does it have? -```r +``` r nz_height_count = aggregate(nz_height, nz, length) nz_height_combined = cbind(nz, count = nz_height_count$elevation) nz_height_combined |> @@ -47,7 +50,7 @@ E3. Generalizing the question to all regions: how many of New Zealand's 16 regio - Bonus: create a table listing these regions in order of the number of points and their name. -```r +``` r # Base R way: nz_height_count = aggregate(nz_height, nz, length) nz_height_combined = cbind(nz, count = nz_height_count$elevation) @@ -63,6 +66,9 @@ nz_height_counts = nz_height_joined |> # Optionally join results with nz geometries: nz_height_combined = left_join(nz, nz_height_counts |> sf::st_drop_geometry()) #> Joining with `by = join_by(Name)` +``` + +``` r # plot(nz_height_combined) # Check: results identical to base R result # Generate a summary table @@ -92,7 +98,7 @@ The starting point of this exercise is to create an object representing Colorado - Create another object representing all the objects that touch (have a shared boundary with) Colorado and plot the result (hint: remember you can use the argument `op = st_intersects` and other spatial relations during spatial subsetting operations in base R). - Bonus: create a straight line from the centroid of the District of Columbia near the East coast to the centroid of California near the West coast of the USA (hint: functions `st_centroid()`, `st_union()` and `st_cast()` described in Chapter 5 may help) and identify which states this long East-West line crosses. -```r +``` r colorado = us_states[us_states$NAME == "Colorado", ] plot(us_states$geometry) plot(colorado$geometry, col = "grey", add = TRUE) @@ -100,7 +106,7 @@ plot(colorado$geometry, col = "grey", add = TRUE) -```r +``` r intersects_with_colorado = us_states[colorado, , op = st_intersects] plot(us_states$geometry, main = "States that intersect with Colorado") plot(intersects_with_colorado$geometry, col = "grey", add = TRUE) @@ -108,7 +114,7 @@ plot(intersects_with_colorado$geometry, col = "grey", add = TRUE) -```r +``` r # Alternative but more verbose solutions # 2: With intermediate object, one list for each state sel_intersects_colorado = st_intersects(us_states, colorado) @@ -121,9 +127,15 @@ sel_intersects_colorado2 #> Sparse geometry binary predicate list of length 1, where the predicate #> was `intersects' #> 1: 2, 3, 9, 19, 37, 39, 45, 49 +``` + +``` r us_states$NAME[unlist(sel_intersects_colorado2)] #> [1] "Arizona" "Colorado" "Kansas" "Oklahoma" "Nebraska" #> [6] "New Mexico" "Utah" "Wyoming" +``` + +``` r # 4: With tidyverse us_states |> @@ -151,6 +163,9 @@ us_states |> #> 6 MULTIPOLYGON (((-109 37, -1... #> 7 MULTIPOLYGON (((-114 42, -1... #> 8 MULTIPOLYGON (((-104 45, -1... +``` + +``` r touches_colorado = us_states[colorado, , op = st_touches] plot(us_states$geometry, main = "States that touch Colorado") plot(touches_colorado$geometry, col = "grey", add = TRUE) @@ -158,22 +173,31 @@ plot(touches_colorado$geometry, col = "grey", add = TRUE) -```r +``` r washington_to_cali = us_states |> filter(grepl(pattern = "Columbia|Cali", x = NAME)) |> st_centroid() |> st_union() |> st_cast("LINESTRING") #> Warning: st_centroid assumes attributes are constant over geometries +``` + +``` r states_crossed = us_states[washington_to_cali, , op = st_crosses] #> although coordinates are longitude/latitude, st_crosses assumes that they are #> planar +``` + +``` r states_crossed$NAME #> [1] "Colorado" "Indiana" "Kansas" #> [4] "Missouri" "Nevada" "West Virginia" #> [7] "California" "District of Columbia" "Illinois" #> [10] "Kentucky" "Ohio" "Utah" #> [13] "Virginia" +``` + +``` r plot(us_states$geometry, main = "States crossed by a straight line\n from the District of Columbia to central California") plot(states_crossed$geometry, col = "grey", add = TRUE) plot(washington_to_cali, add = TRUE) @@ -185,9 +209,12 @@ plot(washington_to_cali, add = TRUE) E5. Use `dem = rast(system.file("raster/dem.tif", package = "spDataLarge"))`, and reclassify the elevation in three classes: low (<300), medium and high (>500). Secondly, read the NDVI raster (`ndvi = rast(system.file("raster/ndvi.tif", package = "spDataLarge"))`) and compute the mean NDVI and the mean elevation for each altitudinal class. -```r +``` r library(terra) #> terra 1.7.71 +``` + +``` r dem = rast(system.file("raster/dem.tif", package = "spDataLarge")) ndvi = rast(system.file("raster/ndvi.tif", package = "spDataLarge")) @@ -211,7 +238,7 @@ E6. Apply a line detection filter to `rast(system.file("ex/logo.tif", package = Plot the result. Hint: Read `?terra::focal()`. -```r +``` r # from the focal help page (?terra::focal()): # Laplacian filter: filter=matrix(c(0,1,0,1,-4,1,0,1,0), nrow=3) # Sobel filters (for edge detection): @@ -236,7 +263,7 @@ E7. Calculate the Normalized Difference Water Index (NDWI; `(green - nir)/(green Use the Landsat image provided by the **spDataLarge** package (`system.file("raster/landsat.tif", package = "spDataLarge")`). Also, calculate a correlation between NDVI and NDWI for this area (hint: you can use the `layerCor()` function). -```r +``` r file = system.file("raster/landsat.tif", package = "spDataLarge") multi_rast = rast(file) @@ -272,6 +299,9 @@ layerCor(two_rasts, fun = "cor") #> ndvi ndwi #> ndvi NaN 1610784 #> ndwi 1610784 NaN +``` + +``` r # correlation -- option 2 two_rasts_df = as.data.frame(two_rasts) @@ -281,12 +311,12 @@ cor(two_rasts_df$ndvi, two_rasts_df$ndwi) -E8. A StackOverflow [post](https://stackoverflow.com/questions/35555709/global-raster-of-geographic-distances) shows how to compute distances to the nearest coastline using `raster::distance()`. +E8. A StackOverflow [post (stackoverflow.com/questions/35555709)](https://stackoverflow.com/questions/35555709/global-raster-of-geographic-distances) shows how to compute distances to the nearest coastline using `raster::distance()`. Try to do something similar but with `terra::distance()`: retrieve a digital elevation model of Spain, and compute a raster which represents distances to the coast across the country (hint: use `geodata::elevation_30s()`). Convert the resulting distances from meters to kilometers. Note: it may be wise to increase the cell size of the input raster to reduce compute time during this operation (`aggregate()`). -```r +``` r # Fetch the DEM data for Spain spain_dem = geodata::elevation_30s(country = "Spain", path = ".", mask = FALSE) @@ -303,6 +333,9 @@ water_mask[water_mask == 0] = NA # Use the distance() function on this mask to get distance to the coast distance_to_coast = distance(water_mask) #> |---------|---------|---------|---------| ========================================= +``` + +``` r # convert distance into km distance_to_coast_km = distance_to_coast / 1000 @@ -315,7 +348,7 @@ plot(distance_to_coast_km, main = "Distance to the coast (km)") E9. Try to modify the approach used in the above exercise by weighting the distance raster with the elevation raster; every 100 altitudinal meters should increase the distance to the coast by 10 km. Next, compute and visualize the difference between the raster created using the Euclidean distance (E7) and the raster weighted by elevation. -```r +``` r # now let's weight each 100 altitudinal meters by an additional distance of 10 km distance_to_coast_km2 = distance_to_coast_km + ((spain_dem / 100) * 10) # plot the result diff --git a/05-geometry-operations.md b/05-geometry-operations.md index 403f9fd..50b5174 100644 --- a/05-geometry-operations.md +++ b/05-geometry-operations.md @@ -3,7 +3,7 @@ -```r +``` r library(sf) library(terra) library(dplyr) @@ -17,7 +17,7 @@ Experiment with different values of `keep` (ranging from 0.5 to 0.00005) for `ms - At what value does the form of the result start to break down for each method, making New Zealand unrecognizable? - Advanced: What is different about the geometry type of the results from `st_simplify()` compared with the geometry type of `ms_simplify()`? What problems does this create and how can this be resolved? -```r +``` r plot(rmapshaper::ms_simplify(st_geometry(nz), keep = 0.5)) plot(rmapshaper::ms_simplify(st_geometry(nz), keep = 0.05)) # Starts to breakdown here at 0.5% of the points: @@ -39,12 +39,18 @@ nz_simple_poly = st_simplify(st_geometry(nz), dTolerance = 10000) |> st_cast("POLYGON") #> Warning in st_cast.MULTIPOLYGON(X[[i]], ...): polygon from first part only #> Warning in st_cast.MULTIPOLYGON(X[[i]], ...): polygon from first part only +``` + +``` r nz_simple_multipoly = st_simplify(st_geometry(nz), dTolerance = 10000) |> st_sfc() |> st_cast("MULTIPOLYGON") plot(nz_simple_poly) length(nz_simple_poly) #> [1] 16 +``` + +``` r nrow(nz) #> [1] 16 ``` @@ -54,7 +60,7 @@ nrow(nz) E2. In the first exercise in Chapter Spatial data operations it was established that Canterbury region had 70 of the 101 highest points in New Zealand. Using `st_buffer()`, how many points in `nz_height` are within 100 km of Canterbury? -```r +``` r canterbury = nz[nz$Name == "Canterbury", ] cant_buff = st_buffer(canterbury, 100000) nz_height_near_cant = nz_height[cant_buff, ] @@ -65,9 +71,12 @@ nrow(nz_height_near_cant) # 75 - 5 more E3. Find the geographic centroid of New Zealand. How far is it from the geographic centroid of Canterbury? -```r +``` r cant_cent = st_centroid(canterbury) #> Warning: st_centroid assumes attributes are constant over geometries +``` + +``` r nz_centre = st_centroid(st_union(nz)) st_distance(cant_cent, nz_centre) # 234 km #> Units: [m] @@ -82,11 +91,14 @@ Hint: you need to use a two-element vector for this transformation. Bonus: create an upside-down map of your country. -```r +``` r world_sfc = st_geometry(world) world_sfc_mirror = world_sfc * c(1, -1) #> Warning in mapply(function(x, y) {: longer argument not a multiple of length of #> shorter +``` + +``` r plot(world_sfc) plot(world_sfc_mirror) @@ -94,6 +106,9 @@ us_states_sfc = st_geometry(us_states) us_states_sfc_mirror = us_states_sfc * c(1, -1) #> Warning in mapply(function(x, y) {: longer argument not a multiple of length of #> shorter +``` + +``` r plot(us_states_sfc) plot(us_states_sfc_mirror) ## nicer plot @@ -116,7 +131,7 @@ E5. Run the code in Section [5.2.6](https://r.geocompx.org/geometry-operations.h - Using base subsetting operators. - Using an intermediary object created with `st_intersection()`\index{vector!intersection}. -```r +``` r p_in_y = p[y] p_in_xy = p_in_y[x] x_and_y = st_intersection(x, y) @@ -133,7 +148,7 @@ E6. Calculate the length of the boundary lines of US states in meters. Which state has the longest border and which has the shortest? Hint: The `st_length` function computes the length of a `LINESTRING` or `MULTILINESTRING` geometry. -```r +``` r us_states9311 = st_transform(us_states, "EPSG:9311") us_states_bor = st_cast(us_states9311, "MULTILINESTRING") us_states_bor$borders = st_length(us_states_bor) @@ -166,6 +181,9 @@ arrange(us_states_bor, borders) #> 8 1018656 [m] MULTILINESTRING ((2422968 3... #> 9 1275538 [m] MULTILINESTRING ((1534988 -... #> 10 1436255 [m] MULTILINESTRING ((1033797 -... +``` + +``` r arrange(us_states_bor, -borders) #> Simple feature collection with 49 features and 7 fields #> Geometry type: MULTILINESTRING @@ -203,7 +221,7 @@ Change its resolution to 0.01 by 0.01 degrees using all of the method available Visualize the results. Can you notice any differences between the results of these resampling methods? -```r +``` r srtm = rast(system.file("raster/srtm.tif", package = "spDataLarge")) rast_template = rast(ext(srtm), res = 0.01) srtm_resampl1 = resample(srtm, y = rast_template, method = "bilinear") diff --git a/06-raster-vector.md b/06-raster-vector.md index 4f84eb2..0e28714 100644 --- a/06-raster-vector.md +++ b/06-raster-vector.md @@ -6,7 +6,7 @@ Some of the following exercises use a vector (`zion_points`) and raster dataset (`srtm`) from the **spDataLarge** package. They also use a polygonal 'convex hull' derived from the vector dataset (`ch`) to represent the area of interest: -```r +``` r library(sf) library(terra) library(spData) @@ -24,7 +24,7 @@ Next, mask `srtm` using these two datasets. Can you see any difference now? How can you explain that? -```r +``` r plot(srtm) plot(st_geometry(zion_points), add = TRUE) plot(ch, add = TRUE) @@ -48,7 +48,7 @@ When would extracting values by buffers be more suitable than by points alone? - Bonus: Implement extraction using the **exactextractr** package and compare the results. -```r +``` r zion_points_buf = st_buffer(zion_points, dist = 90) plot(srtm) plot(st_geometry(zion_points_buf), add = TRUE) @@ -75,7 +75,7 @@ Using these two new objects: - Count numbers of the highest points in each grid cell. - Find the maximum elevation in each grid cell. -```r +``` r nz_height3100 = dplyr::filter(nz_height, elevation > 3100) new_graticule = st_graticule(nz_height3100, datum = "EPSG:2193") plot(st_geometry(nz_height3100), graticule = new_graticule, axes = TRUE) @@ -100,10 +100,13 @@ E4. Aggregate the raster counting high points in New Zealand (created in the pre - Resample the lower resolution raster back to the original resolution of 3 km. How have the results changed? - Name two advantages and disadvantages of reducing raster resolution. -```r +``` r nz_raster_low = raster::aggregate(nz_raster, fact = 2, fun = sum, na.rm = TRUE) res(nz_raster_low) #> [1] 6000 6000 +``` + +``` r nz_resample = resample(nz_raster_low, nz_raster) plot(nz_raster_low) @@ -126,14 +129,14 @@ Disadvantages: E5. Polygonize the `grain` dataset and filter all squares representing clay. -```r +``` r grain = rast(system.file("raster/grain.tif", package = "spData")) ``` - Name two advantages and disadvantages of vector data over raster data. - When would it be useful to convert rasters to vectors in your work? -```r +``` r grain_poly = as.polygons(grain) |> st_as_sf() levels(grain) @@ -142,6 +145,9 @@ levels(grain) #> 1 0 clay #> 2 1 silt #> 3 2 sand +``` + +``` r clay = dplyr::filter(grain_poly, grain == "clay") plot(clay) ``` diff --git a/07-reproj.md b/07-reproj.md index f8974a2..ccb9d7c 100644 --- a/07-reproj.md +++ b/07-reproj.md @@ -3,7 +3,7 @@ -```r +``` r library(sf) library(terra) library(spData) @@ -15,7 +15,7 @@ E1. Create a new object called `nz_wgs` by transforming `nz` object into the WGS - With reference to the bounding box of each object, what units does each CRS use? - Remove the CRS from `nz_wgs` and plot the result: what is wrong with this map of New Zealand and why? -```r +``` r st_crs(nz) #> Coordinate Reference System: #> User input: EPSG:2193 @@ -41,19 +41,34 @@ st_crs(nz) #> UNIT["metre",1, #> AUTHORITY["EPSG","9001"]], #> AUTHORITY["EPSG","2193"]] +``` + +``` r nz_wgs = st_transform(nz, "EPSG:4326") nz_crs = st_crs(nz) nz_wgs_crs = st_crs(nz_wgs) nz_crs$epsg #> [1] 2193 +``` + +``` r nz_wgs_crs$epsg #> [1] 4326 +``` + +``` r st_bbox(nz) #> xmin ymin xmax ymax #> 1090144 4748537 2089533 6191874 +``` + +``` r st_bbox(nz_wgs) #> xmin ymin xmax ymax #> 166.4 -47.3 178.6 -34.4 +``` + +``` r nz_wgs_NULL_crs = st_set_crs(nz_wgs, NA) nz_27700 = st_transform(nz_wgs, "EPSG:27700") par(mfrow = c(1, 3)) @@ -73,7 +88,7 @@ What has changed and why? Try to transform it back into WGS 84 and plot the new object. Why does the new object differ from the original one? -```r +``` r # see https://github.com/r-spatial/sf/issues/509 world_tmerc = st_transform(world, "+proj=tmerc") plot(st_geometry(world_tmerc)) @@ -87,7 +102,7 @@ E3. Transform the continuous raster (`con_raster`) into NAD83 / UTM zone 12N usi What has changed? How does it influence the results? -```r +``` r con_raster = rast(system.file("raster/srtm.tif", package = "spDataLarge")) con_raster_utm12n = project(con_raster, "EPSG:32612", method = "near") con_raster_utm12n @@ -100,6 +115,9 @@ con_raster_utm12n #> name : srtm #> min value : 1024 #> max value : 2892 +``` + +``` r plot(con_raster) plot(con_raster_utm12n) @@ -111,7 +129,7 @@ E4. Transform the categorical raster (`cat_raster`) into WGS 84 using the biline What has changed? How does it influence the results? -```r +``` r cat_raster = rast(system.file("raster/nlcd.tif", package = "spDataLarge")) cat_raster_wgs84 = project(cat_raster, "EPSG:4326", method = "bilinear") cat_raster_wgs84 @@ -124,6 +142,9 @@ cat_raster_wgs84 #> name : levels #> min value : 1 #> max value : 8 +``` + +``` r plot(cat_raster) plot(cat_raster_wgs84) diff --git a/08-read-write-plot.md b/08-read-write-plot.md index 25d859a..b1b2c85 100644 --- a/08-read-write-plot.md +++ b/08-read-write-plot.md @@ -3,7 +3,7 @@ -```r +``` r library(sf) library(terra) ``` @@ -26,15 +26,18 @@ E2. Name at least two differences between the **sf** functions `read_sf()` and ` The differences can be seen by running the following commands `nc = st_read(system.file("shape/nc.shp", package="sf"))` and `nc = read_sf(system.file("shape/nc.shp", package="sf"))` from the function's help (`?st_read`). -```r +``` r read_sf #> function (..., quiet = TRUE, stringsAsFactors = FALSE, as_tibble = TRUE) #> { #> st_read(..., quiet = quiet, stringsAsFactors = stringsAsFactors, #> as_tibble = as_tibble) #> } -#> +#> #> +``` + +``` r nc = st_read(system.file("shape/nc.shp", package="sf")) #> Reading layer `nc' from data source #> `/usr/local/lib/R/site-library/sf/shape/nc.shp' using driver `ESRI Shapefile' @@ -43,13 +46,16 @@ nc = st_read(system.file("shape/nc.shp", package="sf")) #> Dimension: XY #> Bounding box: xmin: -84.3 ymin: 33.9 xmax: -75.5 ymax: 36.6 #> Geodetic CRS: NAD27 +``` + +``` r nc = read_sf(system.file("shape/nc.shp", package="sf")) ``` E3. Read the `cycle_hire_xy.csv` file from the **spData** package as a spatial object (Hint: it is located in the `misc` folder). What is a geometry type of the loaded object? -```r +``` r c_h = read.csv(system.file("misc/cycle_hire_xy.csv", package = "spData")) |> st_as_sf(coords = c("X", "Y")) c_h @@ -75,12 +81,15 @@ c_h E4. Download the borders of Germany using **rnaturalearth**, and create a new object called `germany_borders`. Write this new object to a file of the GeoPackage format. -```r +``` r library(rnaturalearth) germany_borders = ne_countries(country = "Germany", returnclass = "sf") plot(germany_borders) #> Warning: plotting the first 10 out of 168 attributes; use max.plot = 168 to #> plot all +``` + +``` r st_write(germany_borders, "germany_borders.gpkg") #> Writing layer `germany_borders' to data source #> `germany_borders.gpkg' using driver `GPKG' @@ -92,13 +101,16 @@ st_write(germany_borders, "germany_borders.gpkg") E5. Download the global monthly minimum temperature with a spatial resolution of five minutes using the **geodata** package. Extract the June values, and save them to a file named `tmin_june.tif` file (hint: use `terra::subset()`). -```r +``` r library(geodata) gmmt = worldclim_global(var = "tmin", res = 5, path = tempdir()) names(gmmt) #> [1] "wc2.1_5m_tmin_01" "wc2.1_5m_tmin_02" "wc2.1_5m_tmin_03" "wc2.1_5m_tmin_04" #> [5] "wc2.1_5m_tmin_05" "wc2.1_5m_tmin_06" "wc2.1_5m_tmin_07" "wc2.1_5m_tmin_08" #> [9] "wc2.1_5m_tmin_09" "wc2.1_5m_tmin_10" "wc2.1_5m_tmin_11" "wc2.1_5m_tmin_12" +``` + +``` r plot(gmmt) gmmt_june = terra::subset(gmmt, "wc2.1_5m_tmin_06") @@ -110,7 +122,7 @@ writeRaster(gmmt_june, "tmin_june.tif") E6. Create a static map of Germany's borders, and save it to a PNG file. -```r +``` r png(filename = "germany.png", width = 350, height = 500) plot(st_geometry(germany_borders), axes = TRUE, graticule = TRUE) dev.off() @@ -121,7 +133,7 @@ dev.off() E7. Create an interactive map using data from the `cycle_hire_xy.csv` file. Export this map to a file called `cycle_hire.html`. -```r +``` r library(mapview) mapview_obj = mapview(c_h, zcol = "nbikes", legend = TRUE) mapshot(mapview_obj, file = "cycle_hire.html") diff --git a/09-mapping.md b/09-mapping.md index 3da7493..c215bf7 100644 --- a/09-mapping.md +++ b/09-mapping.md @@ -5,7 +5,7 @@ -```r +``` r library(sf) library(terra) library(dplyr) @@ -15,7 +15,7 @@ library(spData) These exercises rely on a new object, `africa`. Create it using the `world` and `worldbank_df` datasets from the **spData** package as follows: -```r +``` r library(spData) africa = world |> filter(continent == "Africa", !is.na(iso_a2)) |> @@ -28,7 +28,7 @@ africa = world |> We will also use `zion` and `nlcd` datasets from **spDataLarge**: -```r +``` r zion = read_sf((system.file("vector/zion.gpkg", package = "spDataLarge"))) nlcd = rast(system.file("raster/nlcd.tif", package = "spDataLarge")) ``` @@ -39,20 +39,26 @@ E1. Create a map showing the geographic distribution of the Human Development In - Name three other mapping packages and an advantage of each. - Bonus: create three more maps of Africa using these three other packages. -```r +``` r # graphics plot(africa["HDI"]) # tmap remotes::install_github("r-tmap/tmap") #> Using github PAT from envvar GITHUB_PAT. Use `gitcreds::gitcreds_set()` and unset GITHUB_PAT in .Renviron (or elsewhere) if you want to use the more secure git credential store instead. -#> Skipping install of 'tmap' from a github remote, the SHA1 (4780aa7e) has not changed since last install. +#> Skipping install of 'tmap' from a github remote, the SHA1 (34173378) has not changed since last install. #> Use `force = TRUE` to force installation +``` + +``` r library(tmap) #> #> Attaching package: 'tmap' #> The following object is masked from 'package:datasets': #> #> rivers +``` + +``` r tm_shape(africa) + tm_polygons("HDI") # ggplot @@ -72,6 +78,9 @@ library(plotly) #> The following object is masked from 'package:graphics': #> #> layout +``` + +``` r g = ggplot() + geom_sf(data = africa, aes(fill = HDI)) ggplotly(g) @@ -79,7 +88,10 @@ ggplotly(g) ```{=html}
- + +``` + +``` r # mapsf library(mapsf) mf_map(x = africa, var = "HDI", type = "choro") @@ -90,7 +102,7 @@ mf_map(x = africa, var = "HDI", type = "choro") E2. Extend the **tmap** created for the previous exercise so the legend has three bins: "High" (`HDI` above 0.7), "Medium" (`HDI` between 0.55 and 0.7) and "Low" (`HDI` below 0.55). - Bonus: improve the map aesthetics, for example by changing the legend title, class labels and color palette. -```r +``` r library(tmap) tm_shape(africa) + tm_polygons("HDI", @@ -106,7 +118,7 @@ E3. Represent `africa`'s subregions on the map. Change the default color palette and legend title. Next, combine this map and the map created in the previous exercise into a single plot. -```r +``` r asubregions = tm_shape(africa) + tm_polygons("subregion", fill.scale = tm_scale_categorical(values = "Set3"), @@ -130,7 +142,7 @@ E4. Create a land cover map of the Zion National Park. - Add a scale bar and north arrow and change the position of both to improve the map's aesthetic appeal - Bonus: Add an inset map of Zion National Park's location in the context of the Utah state. (Hint: an object representing Utah can be subset from the `us_states` dataset.) -```r +``` r tm_shape(nlcd) + tm_raster(col.scale = tm_scale_categorical(values = c("#495EA1", "#AF5F63", "#EDE9E4", "#487F3F", "#EECFA8", "#A4D378", @@ -143,7 +155,7 @@ tm_shape(nlcd) + -```r +``` r # Bonus utah = subset(us_states, NAME == "Utah") utah = st_transform(utah, st_crs(zion)) @@ -174,6 +186,9 @@ library(grid) #> The following object is masked from 'package:terra': #> #> depth +``` + +``` r norm_dim = function(obj){ bbox = st_bbox(obj) width = bbox[["xmax"]] - bbox[["xmin"]] @@ -193,6 +208,9 @@ ins_vp = viewport(width = ins_dim[1] * 0.4, height = ins_dim[2] * 0.4, grid.newpage() print(main, vp = main_vp) #> SpatRaster object downsampled to 1126 by 889 cells. +``` + +``` r pushViewport(main_vp) print(inset, vp = ins_vp) ``` @@ -204,7 +222,7 @@ E5. Create facet maps of countries in Eastern Africa: - With one facet showing HDI and the other representing population growth (hint: using variables `HDI` and `pop_growth`, respectively) - With a 'small multiple' per country -```r +``` r ea = subset(africa, subregion == "Eastern Africa") #1 tm_shape(ea) + @@ -222,7 +240,7 @@ E6. Building on the previous facet map examples, create animated maps of East Af - Showing each country in order - Showing each country in order with a legend showing the HDI -```r +``` r tma1 = tm_shape(ea) + tm_polygons() + tm_facets(by = "name", nrow = 1, ncol = 1) @@ -247,7 +265,7 @@ E7. Create an interactive map of HDI in Africa: - With **leaflet** - Bonus: For each approach, add a legend (if not automatically provided) and a scale bar -```r +``` r # tmap tmap_mode("view") tm_shape(africa) + tm_polygons("HDI") + tm_scalebar() @@ -289,7 +307,7 @@ Note: the input data must be fed into the map earlier to prevent the polygons di E10. Reproduce Figure 9.1 and Figure 9.7 as closely as possible using the **ggplot2** package. -```r +``` r library(ggplot2) ggplot() + geom_sf(data = nz, color = NA) + @@ -324,15 +342,21 @@ Finally, create and compare two maps of the poverty rate: (1) a standard choropl What is the information provided by the first and the second map? How do they differ from each other? -```r +``` r tmap_mode("plot") #> tmap mode set to 'plot' +``` + +``` r library(cartogram) #> #> Attaching package: 'cartogram' #> The following object is masked from 'package:terra': #> #> cartogram +``` + +``` r # prepare the data us = st_transform(us_states, "EPSG:9311") us = left_join(us, us_states_df, by = c("NAME" = "state")) @@ -354,7 +378,7 @@ tmap_arrange(ecm1, ecm2) E12. Visualize population growth in Africa. Next, compare it with the maps of a hexagonal and regular grid created using the **geogrid** package. -```r +``` r library(geogrid) hex_cells = calculate_grid(africa, grid_type = "hexagonal", seed = 25, learning_rate = 0.03) diff --git a/10-gis.md b/10-gis.md index 7481f61..97ff7cb 100644 --- a/10-gis.md +++ b/10-gis.md @@ -3,7 +3,7 @@ -```r +``` r library(sf) library(terra) ``` @@ -11,7 +11,7 @@ library(terra) E1. Compute global solar irradiation for an area of `system.file("raster/dem.tif", package = "spDataLarge")` for March 21 at 11:00 AM using the `r.sun` GRASS GIS through **qgisprocess**. -```r +``` r library(qgisprocess) # enable grass qgis_enable_plugins("grassprovider") @@ -36,7 +36,7 @@ plot(gsi_dem) E2. Compute catchment area\index{catchment area} and catchment slope of `system.file("raster/dem.tif", package = "spDataLarge")` using **Rsagacmd**. -```r +``` r library(Rsagacmd) dem = rast(system.file("raster/dem.tif", package = "spDataLarge")) saga = saga_gis(raster_backend = "terra", vector_backend = "sf") @@ -52,7 +52,7 @@ E3. Continue working on the `ndvi_segments` object created in the SAGA section. Extract average NDVI values from the `ndvi` raster and group them into six clusters using `kmeans()`. Visualize the results. -```r +``` r library(Rsagacmd) saga = saga_gis(raster_backend = "terra", vector_backend = "sf") ndvi = rast(system.file("raster/ndvi.tif", package = "spDataLarge")) @@ -104,7 +104,7 @@ Visualize your result. For example, plot a hillshade\index{hillshade}, the digital elevation model\index{digital elevation model}, your viewshed\index{viewshed} output, and the point. Additionally, give `mapview` a try. -```r +``` r library(rgrass) dem = rast(system.file("raster/dem.tif", package = "spDataLarge")) data(random_points, package = "spDataLarge") @@ -148,7 +148,7 @@ mapview(out, col = "white", map.type = "Esri.WorldImagery") + E5. Use `gdalinfo` via a system call for a raster\index{raster} file stored on disk of your choice. What kind of information you can find there? -```r +``` r link2GI::linkGDAL() our_filepath = system.file("raster/elev.tif", package = "spData") cmd = paste("gdalinfo", our_filepath) @@ -158,7 +158,7 @@ system(cmd) E6. Use `gdalwarp` to decrease the resolution of your raster file (for example, if the resolution is 0.5, change it into 1). Note: `-tr` and `-r` flags will be used in this exercise. -```r +``` r our_filepath = system.file("raster/elev.tif", package = "spData") cmd2 = paste("gdalwarp", our_filepath, "new_elev.tif", "-tr 1 1", "-r bilinear") system(cmd2) @@ -167,7 +167,7 @@ system(cmd2) E7. Query all Californian highways from the PostgreSQL/PostGIS\index{PostGIS} database living in the QGIS\index{QGIS} Cloud introduced in this chapter. -```r +``` r library(RPostgreSQL) conn = dbConnect(drv = PostgreSQL(), dbname = "rtafdf_zljbqm", host = "db.qgiscloud.com", @@ -185,7 +185,7 @@ E8. The `ndvi.tif` raster (`system.file("raster/ndvi.tif", package = "spDataLarg Use **rstac**, **gdalcubes**, and **terra** to download Sentinel-2 images for the same area from 2020-08-01 to 2020-10-31, calculate its NDVI, and then compare it with the results from `ndvi.tif`. -```r +``` r library(rstac) library(gdalcubes) ?spDataLarge::ndvi.tif diff --git a/11-algorithms.md b/11-algorithms.md index 2d42640..e30adfc 100644 --- a/11-algorithms.md +++ b/11-algorithms.md @@ -5,7 +5,7 @@ The solutions assume the following packages are attached (other packages will be attached when needed): -```r +``` r library(sf) ``` @@ -35,7 +35,7 @@ E2. In the geometric algorithms section we calculated that the area and geograph - Reproduce the results on your own computer with reference to the script [`11-centroid-alg.R`](https://github.com/geocompx/geocompr/blob/main/code/11-centroid-alg.R), an implementation of this algorithm (bonus: type out the commands - try to avoid copy-pasting). - Are the results correct? Verify them by converting `poly_mat` into an `sfc` object (named `poly_sfc`) with `st_polygon()` (hint: this function takes objects of class `list()`) and then using `st_area()` and `st_centroid()`. -```r +``` r # We can verify the answer by converting `poly_mat` into a simple feature collection # as follows, which shows the calculations match: x_coords = c(10, 20, 12, 0, 0, 10) @@ -50,7 +50,7 @@ sf::st_centroid(poly_sfc) E3. It was stated that the algorithm\index{algorithm} we created only works for *convex hulls*. Define convex hulls\index{convex hull} (see the geometry operations chapter) and test the algorithm on a polygon that is *not* a convex hull. -```r +``` r x_coords = c(10, 20, 12, 0, 0, 5, 10) y_coords = c(0, 15, 20, 10, 0, 5, 0) plot(x_coords, y_coords, type = "l") @@ -77,7 +77,7 @@ Further extend the function by creating a version (e.g., called `poly_centroid_s - What error message do you get when you try to run `poly_centroid_sf(poly_mat)`? -```r +``` r poly_centroid_sf = function(x) { stopifnot(is(x, "sf")) xcoords = sf::st_coordinates(x) diff --git a/12-spatial-cv.md b/12-spatial-cv.md index a69e99b..2119dc8 100644 --- a/12-spatial-cv.md +++ b/12-spatial-cv.md @@ -5,7 +5,7 @@ The solutions assume the following packages are attached (other packages will be attached when needed): -```r +``` r library(dplyr) # library(kernlab) library(mlr3) @@ -27,7 +27,7 @@ E1. Compute the following terrain attributes from the `elev` dataset loaded with - Catchment area -```r +``` r # attach data dem = terra::rast(system.file("raster/ta.tif", package = "spDataLarge"))$elev @@ -66,7 +66,7 @@ ta = c(ta, dem, log10_carea) E2. Extract the values from the corresponding output rasters to the `lsl` data frame (`data("lsl", package = "spDataLarge"`) by adding new variables called `slope`, `cplan`, `cprof`, `elev` and `log_carea`. -```r +``` r # attach terrain attribute raster stack (in case you have skipped the previous # exercise) data("lsl", package = "spDataLarge") @@ -80,7 +80,7 @@ lsl[, names(ta)] = terra::extract(ta, lsl[, c("x", "y")]) |> E3. Use the derived terrain attribute rasters in combination with a GLM to make a spatial prediction map similar to that shown in Figure 12.2. Running `data("study_mask", package = "spDataLarge")` attaches a mask of the study area. -```r +``` r # attach data (in case you have skipped exercises 1) and 2) # landslide points with terrain attributes and terrain attribute raster stack data("lsl", "study_mask", package = "spDataLarge") @@ -128,7 +128,7 @@ When doing so, keep in mind that the computation can take very long, probably se This, of course, depends on your system. Computation time will be shorter the more RAM and cores you have at your disposal. -```r +``` r # attach data (in case you have skipped exercises 1) and 2) data("lsl", package = "spDataLarge") # landslide points with terrain attributes @@ -219,7 +219,7 @@ E5. Model landslide susceptibility using a quadratic discriminant analysis (QDA) Assess the predictive performance of the QDA. What is the a difference between the spatially cross-validated mean AUROC value of the QDA and the GLM? -```r +``` r # attach data (in case you have skipped exercise 4) bmr = readRDS("extdata/12-bmr.rds") @@ -233,7 +233,7 @@ E6. Run the SVM without tuning the hyperparameters. Use the `rbfdot` kernel with $\sigma$ = 1 and *C* = 1. Leaving the hyperparameters unspecified in **kernlab**'s `ksvm()` would otherwise initialize an automatic non-spatial hyperparameter tuning. -```r +``` r # attach data (in case you have skipped exercise 4) bmr = readRDS("extdata/12-bmr.rds") # plot your result diff --git a/13-transport.md b/13-transport.md index 15985d7..2cad5c8 100644 --- a/13-transport.md +++ b/13-transport.md @@ -5,7 +5,7 @@ -```r +``` r library(sf) library(spDataLarge) ``` @@ -33,7 +33,7 @@ E3. What proportion of trips represented in the `desire_lines` are accounted for E4. The analysis presented in this chapter is designed for teaching how geocomputation methods can be applied to transport research. If you were doing this for real, in government or for a transport consultancy, what top 3 things would you do differently? -```r +``` r # Higher level of geographic resolution. # Use cycle-specific routing services. # Identify key walking routes. diff --git a/14-location.md b/14-location.md index c519973..f0060a2 100644 --- a/14-location.md +++ b/14-location.md @@ -5,7 +5,7 @@ The solutions assume the following packages are attached (other packages will be attached when needed): -```r +``` r library(sf) library(dplyr) library(purrr) @@ -22,7 +22,7 @@ This takes 30 seconds on a machine with 16 GB RAM. Use `dplyr::as_tibble()` to convert it into a tibble. Build an inhabitant raster, aggregate it to a cell resolution of 1 km, and compare the difference with the inhabitant raster (`inh`) we have created using class mean values. -```r +``` r # Coarse inhabitant raster (1 km resolution) #******************************************* @@ -89,7 +89,7 @@ terra::global((abs(inh_fine - inh_coarse) > 5000), fun = "sum", na.rm = TRUE) E2. Suppose our bike shop predominantly sold electric bikes to older people. Change the age raster accordingly, repeat the remaining analyses and compare the changes with our original result. -```r +``` r # Here, we assue that you have already created `input_ras` in the first exercise. # attach further necessary data data("metro_names", "shops", package = "spDataLarge") diff --git a/15-eco.md b/15-eco.md index cf7803f..53643c0 100644 --- a/15-eco.md +++ b/15-eco.md @@ -5,7 +5,7 @@ The solutions assume the following packages are attached (other packages will be attached when needed): -```r +``` r library(sf) library(terra) library(data.table) @@ -28,7 +28,7 @@ E1. Run a NMDS\index{NMDS} using the percentage data of the community matrix. Report the stress value and compare it to the stress value as retrieved from the NMDS using presence-absence data. What might explain the observed difference? -```r +``` r data("comm", package = "spDataLarge") pa = vegan::decostand(comm, "pa") pa = pa[rowSums(pa) != 0, ] @@ -60,7 +60,7 @@ Finally, construct a response-predictor matrix. The scores of the first NMDS\index{NMDS} axis (which were the result when using the presence-absence community matrix) rotated in accordance with elevation represent the response variable, and should be joined to `random_points` (use an inner join). To complete the response-predictor matrix, extract the values of the environmental predictor raster object to `random_points`. -```r +``` r # first compute the terrain attributes we have also used in the chapter library(dplyr) library(terra) @@ -134,7 +134,7 @@ Parallelize\index{parallelization} the tuning level. Report the mean RMSE\index{RMSE} and use a boxplot to visualize all retrieved RMSEs. Please not that this exercise is best solved using the mlr3 functions `benchmark_grid()` and `benchmark()` (see https://mlr3book.mlr-org.com/perf-eval-cmp.html#benchmarking for more information). -```r +``` r library(dplyr) library(future) library(mlr3) diff --git a/404.html b/404.html index a3f49cd..19823ee 100644 --- a/404.html +++ b/404.html @@ -97,7 +97,7 @@

Note: Second Edition is under construction 🏗