|
| 1 | +--- |
| 2 | +title: "Raster Operations" |
| 3 | +description: | |
| 4 | + Learn how to 1) obtain a template raster from the `{d6geodata}` package, 2) expand the raster in different ways, 3) rasterize vector data, and 4) extract information from a raster using vector data. |
| 5 | + The methods described involve the use of packages like `{terra}` and `{sf}`, and include examples and plots to illustrate the processes. Additionally, we show how to use the `{tidyterra}` package for plotting raster data with `{ggplot2}`. |
| 6 | + |
| 7 | +preview: ./img/wiki/preview-rastertemplate.png |
| 8 | +categories: |
| 9 | + - tutorial |
| 10 | + - spatial |
| 11 | + - sf |
| 12 | + - terra |
| 13 | +author: |
| 14 | + - name: Moritz Wenzler-Meya |
| 15 | + affiliation: IZW Berlin |
| 16 | + affiliation_url: https://ecodynizw.github.io/ |
| 17 | +date: 2024-03-05 |
| 18 | +output: |
| 19 | + distill::distill_article: |
| 20 | + self_contained: false |
| 21 | + toc: true |
| 22 | +draft: false |
| 23 | +--- |
| 24 | + |
| 25 | +```{r setup, include=FALSE} |
| 26 | +knitr::opts_chunk$set(echo = TRUE) ## keep this, add other settings if needed |
| 27 | +``` |
| 28 | + |
| 29 | +## Setup {#setup} |
| 30 | + |
| 31 | +Install the `{d6geodata}` library and load all necessary libraries. |
| 32 | + |
| 33 | +```{r} |
| 34 | +#remotes::install_github("EcoDynIZW/d6geodata") |
| 35 | +library(d6geodata) |
| 36 | +library(terra) |
| 37 | +library(ggplot2) |
| 38 | +library(sf) |
| 39 | +library(patchwork) |
| 40 | +library(dplyr) |
| 41 | +
|
| 42 | +theme_set( |
| 43 | + theme(axis.text.x = element_text(size = 8), |
| 44 | + axis.text.y = element_text(size = 8)) |
| 45 | +) |
| 46 | +``` |
| 47 | + |
| 48 | + |
| 49 | +## Raster Template {#template} |
| 50 | + |
| 51 | +First get the template raster. The origin is the extent of Berlin with the projection LAEA (EPSG:3035). |
| 52 | + |
| 53 | +```{r template} |
| 54 | +temp <- d6geodata::temp_ras(1000) # resolution is 1000m |
| 55 | +values(temp) <- 1 # set a dummy value for visualization purposes |
| 56 | +
|
| 57 | +temp |
| 58 | +``` |
| 59 | + |
| 60 | +We load Berlin districts data set from our cloud to show the borders of Berlin. |
| 61 | + |
| 62 | +```{r berlin borders} |
| 63 | +berlin <- d6geodata::get_geodata(data_name = "districs_berlin_2022_poly_03035_gpkg", |
| 64 | + path_to_cloud = "E:/PopDynCloud", |
| 65 | + download_data = FALSE) %>% |
| 66 | + st_union() %>% # using `st_union()` to combine geometries |
| 67 | + st_cast("POLYGON") |
| 68 | +
|
| 69 | +p_berlin <- geom_sf(data = berlin, alpha = 0, color = "black", lwd = 0.7) |
| 70 | +``` |
| 71 | + |
| 72 | +Now we plot the map. |
| 73 | + |
| 74 | +```{r plot template} |
| 75 | +p1 <- d6geodata::plot_qualitative_map(temp) # you can use this build-in plot function from the `{d6geodata}` package |
| 76 | + |
| 77 | +p1 + p_berlin |
| 78 | +``` |
| 79 | + |
| 80 | + |
| 81 | +## Expand Raster {#expand} |
| 82 | + |
| 83 | +### Expand On All Sides |
| 84 | + |
| 85 | +If you want to expand the raster (similar to a buffer around a polygon) you can just add cells to all sides. You can do this by adding an integer number in the y argument in the function `extend` of the `{terra}` package. |
| 86 | + |
| 87 | +```{r} |
| 88 | +# expand the map |
| 89 | +temp_exp_all <- terra::extend(x = temp, # template raster as input |
| 90 | + y = 10, # number of cells to add |
| 91 | + fill = 2) # value for new cells |
| 92 | +
|
| 93 | +# plot the map again |
| 94 | +p2 <- d6geodata::plot_qualitative_map(temp_exp_all) |
| 95 | +p2 + p_berlin |
| 96 | +``` |
| 97 | + |
| 98 | +### Expand On Two Sides |
| 99 | + |
| 100 | +You can also expand the raster in one or two different directions only by adding cells on two opposite sides. |
| 101 | + |
| 102 | +```{r} |
| 103 | +temp_exp_tb_sides <- terra::extend(x = temp, |
| 104 | + y = c(10, 0), # add 10 cells on top and below |
| 105 | + fill = 2,) |
| 106 | +p3 <- d6geodata::plot_qualitative_map(temp_exp_tb_sides) |
| 107 | +p3 + p_berlin |
| 108 | +``` |
| 109 | + |
| 110 | +### Expand On One Side With A Custom Extent |
| 111 | + |
| 112 | +In this case we use a workaround by using a custom extent. Here we are adding cells on the left (western) side by using the *xmin* of the larger extend. Xmax, ymin, ymax are from the given extent. |
| 113 | + |
| 114 | +```{r} |
| 115 | +temp_exp_1_sides <- terra::extend(x = temp, |
| 116 | + y = ext(ext(temp_exp_all)[1], # xmin |
| 117 | + ext(temp)[2], # xmax |
| 118 | + ext(temp)[3], # ymin |
| 119 | + ext(temp)[4]),# ymax |
| 120 | + fill = 2) |
| 121 | +
|
| 122 | +p4 <- d6geodata::plot_qualitative_map(temp_exp_1_sides) |
| 123 | +p4 + p_berlin |
| 124 | +``` |
| 125 | + |
| 126 | +### Combined Plot |
| 127 | + |
| 128 | +Here you see a combination of all plots made to compare them directly. |
| 129 | + |
| 130 | +```{r, preview = TRUE} |
| 131 | +# arrange plots in a 2x2 grid |
| 132 | +(p1 + p_berlin + theme(legend.position="none") + p2 + p_berlin) / (p3 + p_berlin + p4 + p_berlin) + |
| 133 | + plot_layout(guides = "collect") + |
| 134 | + plot_annotation(tag_levels = "1", tag_prefix = "(P", tag_suffix = ")") # add tag to every plot |
| 135 | +``` |
| 136 | + |
| 137 | +## Rasterize Vector Data {#rasterize} |
| 138 | + |
| 139 | +With `vect()` you can make a spatVector (similar to a shapefile) from an extent object. |
| 140 | +Afterwards, we can use this spatVector to rasterize the data. This works similar with an sf object. |
| 141 | + |
| 142 | +```{r plot} |
| 143 | +# create spatVector |
| 144 | +temp_vec <- terra::vect(ext(temp), crs(temp)) # spatVector out of smallest raster extent |
| 145 | +
|
| 146 | +# rasterize spatVector |
| 147 | +temp_rast <- rasterize(temp_vec, # vector data |
| 148 | + temp_exp_all %>% disagg(10), |
| 149 | + # raster data with larger extent, disaggregated to 100m |
| 150 | + background = 2) # set to 2 to visualize the difference |
| 151 | +
|
| 152 | +temp_rast |
| 153 | +``` |
| 154 | + |
| 155 | + |
| 156 | +### Rasterized Plot |
| 157 | + |
| 158 | +View the results. |
| 159 | + |
| 160 | +```{r} |
| 161 | +d6geodata::plot_qualitative_map(temp_rast) |
| 162 | +``` |
| 163 | + |
| 164 | +## Extract With Vector Data {#extract} |
| 165 | + |
| 166 | +Imagine you have a track of an animal and you want to know what information lays in this track. In this case, you can use your line and extract the values from a raster below. There are several ways to this but the easiest way is to use the `extract()` function form the `{terra}` package to get the values laying under the vector data. |
| 167 | + |
| 168 | +For this, you can use any vector data (points, lines, polygons) with the same projection and extent to extract values from a raster. The `extract()` function uses the raster as first input and the vector data as second. Depending on the type of data you are using you can define a function for the extraction like min, max or mean. 'Cells' gives you the call ID, 'xy' the coordinates and 'ID' the row number of your extraction. |
| 169 | +As mentioned, it is important that both the vector and the raster have the same projection. |
| 170 | + |
| 171 | +### Line For Extraction |
| 172 | + |
| 173 | +For this we are just creating a dummy line with coordinates laying within the raster |
| 174 | + |
| 175 | +```{r} |
| 176 | +# create points |
| 177 | +pts <- tibble(x = c(4528790, 4576890), # a point out of two coordinates from the raster |
| 178 | + y = c(3271740, 3272140)) |
| 179 | +
|
| 180 | +# create line from points |
| 181 | +dummy_line <- st_as_sf(pts, # a line made out of the point data |
| 182 | + coords = c("x", "y"), # specify columns of xy coordinates |
| 183 | + crs = st_crs(temp)$wkt, # using the same projection as for the template raster |
| 184 | + sf_column_name = "geometry", |
| 185 | + remove = FALSE) %>% |
| 186 | + summarise(do_union = FALSE) %>% # this part is for creating the lines out of the points |
| 187 | + st_cast("LINESTRING") %>% # this as well |
| 188 | + mutate(id = 1:n()) # add an id |
| 189 | +``` |
| 190 | + |
| 191 | +### Plot Of Raster And Line |
| 192 | + |
| 193 | +```{r} |
| 194 | +p5 <- d6geodata::plot_qualitative_map(temp_rast) + # first plot raster |
| 195 | + geom_sf(data = dummy_line) # plot line on top of the raster |
| 196 | +
|
| 197 | +p5 |
| 198 | +
|
| 199 | +``` |
| 200 | + |
| 201 | +### Extraction From Line |
| 202 | + |
| 203 | +Here we finally extract the data and view the result with a table. |
| 204 | + |
| 205 | +```{r} |
| 206 | +ext_tab <- terra::extract(x = temp_rast, # template raster as first input |
| 207 | + y = dummy_line, # dummy line as second input |
| 208 | + fun=NULL, # here you can set a function to summarise the data directly |
| 209 | + method="simple", |
| 210 | + cells=FALSE, # here you can get the cell number as well if cell = TRUE |
| 211 | + xy=FALSE, # here the xy coordinates if xy = TRUE |
| 212 | + ID=TRUE # and the id in front of each row |
| 213 | + ) |
| 214 | +``` |
| 215 | + |
| 216 | +As you already can see on the plot, the largest part of the line lays within the purple area (value = 1). This function can be used to know on which land use class your track lays, for example. |
| 217 | + |
| 218 | +```{r} |
| 219 | +table(ext_tab$layer) |
| 220 | +``` |
| 221 | + |
| 222 | +## Visualize Data With Tidyterra {#tidyterra} |
| 223 | + |
| 224 | +There are several ways to plot raster data with ggplot and one relatively new way is to do it with the package `{tidyterra}`. It has some well developed `{ggplot}` functions, but it cannot always be combined with the base functions of `{ggplot}`. If you use this package you may have to stay within the package functions. |
| 225 | + |
| 226 | + |
| 227 | +```{r} |
| 228 | +library(tidyterra) |
| 229 | +
|
| 230 | +ggplot() + |
| 231 | + geom_spatraster(data = temp_rast) + # use geom_spatraster() to plot the raster data with {tidyterra} |
| 232 | + p_berlin # add vector data of Berlin |
| 233 | +
|
| 234 | +# same a before, but add some color to the plot |
| 235 | +ggplot() + |
| 236 | + geom_spatraster(data = temp_rast) + |
| 237 | + scale_fill_whitebox_c(palette = "purple") + |
| 238 | + p_berlin |
| 239 | +
|
| 240 | +``` |
| 241 | + |
| 242 | + |
| 243 | +## Summary {#summary} |
| 244 | + |
| 245 | +You learned how to extend a raster and how to extract data from a raster by using `{sf}` and `{terra}`. Of course there are several ways to do this, but this the most convenient way!!! |
0 commit comments