# INBO CODING CLUB 27 June 2023 Welcome! ## Share your code snippet If you want to share your code snippet, copy paste your snippet within a section of three backticks (```): As an **example**: ``` library(tidyverse) ``` (*you can copy paste this example and add your code further down*) ## Yellow sticky notes No yellow sticky notes online. Put your name + " | " and add a "*" each time you solve a challenge (see below). ## Participants Name | Challenges --- | --- Damiano Oldoni | Pieter Huybrechts | *** Anja Leyman | ** Leen Govaere | Nele Mullens | ** Jonas Bertels | ** Pieter Verschelde | ** Adriaan Seynaeve| ## Challenge 1 ### Pieter H's solution ``` r # 1 lepidoptera_df <- read_tsv("./data/20230627/20230627_lepidoptera_iNaturalist_2023.csv") ## st_as_sf() will try to convert an object to an sf, sometimes you need to ## provide some extra information lepidoptera_sf <- st_as_sf(lepidoptera_df, coords = c("decimalLongitude", "decimalLatitude"), crs = 4326 ) # 2 st_layers("data/20230627/20230627_protected_areas.gpkg") # 3 prot_areas <- st_read("data/20230627/20230627_protected_areas.gpkg", layer = "ps_hbtrl") ## geopackages are really handy, they can contain a lot of extra information in ## just one file! # 4 st_crs(prot_areas) # 5 ## not exactly the same waldo::compare( st_crs(prot_areas), st_crs(lepidoptera_sf) ) ## but close enough really, only the user provided string is different st_crs(prot_areas) == st_crs(lepidoptera_sf) # 6 provinces_be <- readRDS(file = "./data/20230627/20230627_provinces_be.rds") class(provinces_be) st_as_sf(provinces_be) st_as_sf(provinces_be) %>% mapview() # 7 provinces_fl <- st_as_sf(provinces_be) %>% dplyr::filter(TX_RGN_DESCR_NL == "Vlaams Gewest") mapview(provinces_fl) ``` ## Challenge 2 ### Pieter H's Solution ``` r # 1 ## Transform both prot_areas and lepidoptera to the European Terrestrial ## Reference System 1989 (EPSG: 3035) lepidoptera_3035 <- st_transform(lepidoptera_sf, 3035) ## let's check if it worked st_crs(lepidoptera_3035) mapview(lepidoptera_3035) prot_areas_3035 <- st_transform(prot_areas, 3035) ## let's check if it worked st_crs(prot_areas_3035) mapview(prot_areas_3035) # 2 ## Write the transformed data as a geopackage file called ## prot_areas_and_lepidoptera_3035.gpkg with two layers: the first called ## prot_areas, containing the protected areas, the second layer, ## lepidoptera_obs, containing the observations of lepidoptera st_write( prot_areas_3035, dsn = file.path("data", "20230627", "prot_areas_and_lepidoptera_3035.gpkg"), layer = "prot_areas" ) st_write( lepidoptera_3035, dsn = file.path("data", "20230627", "prot_areas_and_lepidoptera_3035.gpkg"), layer = "lepidoptera_obs" ) # 3 ## Due to spatial uncertainty (gridded data, GPS uncertainty, etc.) observations ## should not be idealized as points in space, but as circles. Create such ## circles using the values stored in column coordinateUncertaintyInMeters of ## lepidoptera_3035. Call this sf object lepidoptera_3035_circles. ## You could probably get away with not specifying the units lepidoptera_3035_circles <- st_buffer( lepidoptera_3035, dist = units::set_units(lepidoptera_3035$coordinateUncertaintyInMeters, m) ) ``` ### Nele's Solution ``` r # 2.1 lepidoptera_3035 <- st_transform(lepidoptera_sf, epsg = 3035) prot_areas_3035 <- st_transform(prot_areas, epsg = 3035) # 2.2 st_write(prot_areas_3035, dsn = "data/20230627/prot_areas_and_lepidoptera_3035.gpkg", layer = "prot_areas") st_write(lepidoptera_3035, dsn = "data/20230627/prot_areas_and_lepidoptera_3035.gpkg", layer = "lepidoptera_obs") # 2.3 lepidoptera_3035_circles <- st_buffer( lepidoptera_3035, dist = lepidoptera_3035$coordinateUncertaintyInMeters, endCapStyle="ROUND" ) ``` ## Challenge 3 ### Pieter H's Solution ``` r # 1 ## Which observations in lepidoptera_3035 (points), are contained in which ## protected area? st_contains(prot_areas_3035, lepidoptera_3035) lepidoptera_3035 %>% mutate(in_prot_area = purrr::map_dbl( st_within(lepidoptera_3035,prot_areas_3035), ~ifelse(is.null(.x),NA,.x) ) ) %>% filter(!is.na(in_prot_area)) # 2 ## But we should maybe better check which observations, as circles (!), ## intersect each protected area. How to do it? st_intersects(lepidoptera_3035_circles, prot_areas_3035) # as a filtered tibble lepidoptera_3035[ !purrr::map_lgl( st_intersects(lepidoptera_3035_circles, prot_areas_3035), rlang::is_empty),] ## check results mapview( list(intersects = lepidoptera_3035[!purrr::map_lgl( st_intersects(lepidoptera_3035_circles, prot_areas_3035), rlang::is_empty), ], prot_areas = prot_areas_3035), col.regions = c("red", "green") ) # 3 ## So, how many observations as circles intersect the Sonian Forest ## ("Zoniënwoud")? ## Method 1: spatial filtering sonian_forest <- filter(prot_areas_3035, NAAM == "Zoniënwoud") st_filter(lepidoptera_3035_circles, sonian_forest, .predicate = st_intersects) # you could also set st_contains for example ## Method 2: by using dplyr filtering, way more complicated lepidoptera_in_prot_area_circl <- lepidoptera_3035 %>% mutate(in_prot_area_circl = purrr::map_dbl( st_intersects(lepidoptera_3035_circles, prot_areas_3035), ~ifelse(is.null(.x),NA,.x) ) ) %>% filter(!is.na(in_prot_area_circl)) ## what is the number of Zoniënwoud? filter(mutate(prot_areas_3035, id = row_number()), NAAM == "Zoniënwoud") %>% pull(id) ## now we can filter on this id, it's probably better to use dplyr joins, and to ## filter on the name directly filter(lepidoptera_in_prot_area_circl, in_prot_area_circl == 29) ## Let's have a look: mapview(list(lepidoptera = filter(lepidoptera_in_prot_area_circl, in_prot_area_circl == 29), sonian_forest = filter(mutate(prot_areas_3035,id = row_number()), NAAM == "Zoniënwoud")), col.regions = c("springgreen", "slateblue")) ## add the uncertaintly, you could filter this one down as well.. mapview(list(lepidoptera = filter(lepidoptera_in_prot_area_circl, in_prot_area_circl == 29), sonian_forest = filter(mutate(prot_areas_3035,id = row_number()), NAAM == "Zoniënwoud"), uncert = lepidoptera_3035_circles), col.regions = c("cyan2", "wheat4", "red")) # 4 ## Sometimes it's interesting to calculate the centroid of a polygon, e.g. for ## visualizations. Easy by using sf function st_centroids(). However, you get an ## error while calculating the centroids of prot_areas. What does it mean? How ## to solve the problem? st_centroid(prot_areas) ## because they aren't all valid geometries! st_is_valid(prot_areas) ## but reprojecting has seemed to solve it mapview(list(centroid = st_centroid(prot_areas_3035), area = prot_areas_3035), zcol = "NAAM", legend = FALSE ) ## or you could use a function to magically make geometries valid st_make_valid(prot_areas) %>% st_is_valid() # 5 provinces_be_sf <- st_as_sf(provinces_be) ## How to get the portion of protected areas located in province Antwerp? st_intersection( st_make_valid(prot_areas), filter(provinces_be_sf, TX_PROV_DESCR_NL == "Provincie Antwerpen") ) %>% mapview(zcol = "NAAM", legend = FALSE) # 6 ## How to get the union of all the protected areas? How such union is expressed ## in sf? ## st_combine returns a single, combined geometry, with no resolved boundaries; returned geometries may well be invalid. ## If y is missing, st_union(x) returns a single geometry with resolved boundaries, else the geometries for all unioned pairs of x[i] and y[j]. st_union(prot_areas_3035) %>% mapview() ## but is invalid st_combine(prot_areas_3035) %>% mapview() st_combine(prot_areas_3035) %>% st_is_valid()` ``` ## Bonus challenge