Segmenting Wildboar trajectories

Import the dataset

library(readr)

wildschwein <- read_csv("wildschwein_BE_2056.csv")
Rows: 51246 Columns: 6
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr  (2): TierID, TierName
dbl  (3): CollarID, E, N
dttm (1): DatetimeUTC

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
library(sf)
Linking to GEOS 3.12.2, GDAL 3.11.4, PROJ 9.4.1; sf_use_s2() is TRUE
wildschwein_sf <- st_as_sf(wildschwein, coords = c("E", "N"), crs = 2056, remove = FALSE)

library(dplyr)

Attaching package: 'dplyr'

The following objects are masked from 'package:stats':

    filter, lag

The following objects are masked from 'package:base':

    intersect, setdiff, setequal, union
# only get the data from sabi between 1 and 3 of July 2015
sabi <- filter(wildschwein_sf, TierName == "Sabi", DatetimeUTC > "2015-07-01", DatetimeUTC < "2015-07-03")

# visualize the result

library(ggplot2)

ggplot(sabi) +
  geom_sf() +
  geom_path(aes(E, N))

Step a): Specify a temporal window v

v is 60 minutes, our sampling interval is 15 minutes. So we need to calculate the following distances:

  • pos[n-2] to pos[n]
  • pos[n-1] to pos[n]
  • pos[n] to pos[n+1]
  • pos[n] to pos[n+2]
distance_by_element <- function(later, now) {
  as.numeric(
    st_distance(later, now, by_element = TRUE)
  )
}

sabi <- sabi |> 
  mutate(
    nMinus2 = distance_by_element(lag(geometry, n = 2), geometry),
    nMinus1 = distance_by_element(lag(geometry, n = 1), geometry),
    nPlus1 = distance_by_element(lead(geometry, n = 1), geometry),
    nPlus2 = distance_by_element(lead(geometry, n = 2), geometry)
  )


sabi <- sabi |> 
  rowwise() |> 
  mutate(
    stepMean = mean(c(nMinus2, nMinus1, nPlus1, nPlus2), na.rm = FALSE)
  ) |> 
  ungroup()

sabi
Simple feature collection with 192 features and 11 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: 2569724 ymin: 1204916 xmax: 2570927 ymax: 1205957
Projected CRS: CH1903+ / LV95
# A tibble: 192 × 12
   TierID TierName CollarID DatetimeUTC                E        N
   <chr>  <chr>       <dbl> <dttm>                 <dbl>    <dbl>
 1 002A   Sabi        12275 2015-06-30 22:00:13 2569972. 1205366.
 2 002A   Sabi        12275 2015-06-30 22:16:06 2569975. 1205637.
 3 002A   Sabi        12275 2015-06-30 22:30:19 2570266. 1205857.
 4 002A   Sabi        12275 2015-06-30 22:45:13 2570208. 1205913.
 5 002A   Sabi        12275 2015-06-30 23:00:10 2570247. 1205731.
 6 002A   Sabi        12275 2015-06-30 23:15:17 2570512. 1205279.
 7 002A   Sabi        12275 2015-06-30 23:30:38 2570684. 1205103.
 8 002A   Sabi        12275 2015-06-30 23:45:16 2570526. 1205051.
 9 002A   Sabi        12275 2015-07-01 00:00:10 2570532. 1205044.
10 002A   Sabi        12275 2015-07-01 00:15:14 2570530. 1205059.
# ℹ 182 more rows
# ℹ 6 more variables: geometry <POINT [m]>, nMinus2 <dbl>, nMinus1 <dbl>,
#   nPlus1 <dbl>, nPlus2 <dbl>, stepMean <dbl>

Step c) remove static points

# fist, specify a threshold. For a simple approach, we'll use the "mean" value as a threshold value
# to differentiate between stops and moves
threshold <- mean(sabi$stepMean, na.rm = TRUE)

sabi <- sabi |> 
  mutate(
    static = stepMean < threshold
  )




ggplot(sabi) +
  geom_path(aes(E, N)) +
  geom_sf(aes(color = static)) 

sabi_move <- sabi |> 
  filter(!static)


ggplot(sabi_move) +
  geom_path(aes(E, N)) +
  geom_sf(aes(color = static))