Hello Chris,
I’m currently working on estimating home ranges of adult kestrels using the ctmm package, and this is my first time using ctmm. I have cleaned my data, converted the cleaned dataset to a telemetry object, and performed variogram calculation and model selection. I am only working with data from the breeding season, so migration should not be an issue.
However, my main problem is interpreting the variogram plots. I am unsure if these patterns indicate range residency or not, and I would greatly appreciate guidance on whether I can reliably continue with model fitting and home range estimation based on these plots.
Thank you very much!
Best regards, Annika
Below is my R workflow for reference:
data <- movebank_download_study(
study_id = 3066197013,
sensor_type_id = "sigfox-geolocation",
timestamp_start = as.POSIXct("2024-03-20 00:00:00", tz = "UTC"),
timestamp_end = as.POSIXct("2024-09-22 23:59:59", tz = "UTC"),
individual_local_identifier = c("Kestrel_BP")
)
#================================================#
### 2. Data Preparation & Cleaning
#================================================#
# Check track structure
mt_is_track_id_cleaved(data)
data <- dplyr::arrange(data, mt_track_id(data))
mt_is_time_ordered(data)
data <- dplyr::arrange(data, mt_track_id(data), mt_time(data))
# Check for duplicates and empty coordinates
mt_has_unique_location_time_records(data)
data <- data[!duplicated(mt_time(data)),
mt_has_no_empty_points(data)
data <- dplyr::filter(data, !sf::st_is_empty(data))
# Filter locations: only >2 connected antennas
data_duplicates <- data %>%
filter(str_count(sigfox_duplicates, "\\|") >= 2)
# Extract coordinates
if ("geometry" %in% colnames(data_duplicates)) {
coords <- st_coordinates(data_duplicates)
data_duplicates$longitude <- coords[, 1]
data_duplicates$latitude <- coords[, 2]
data_duplicates$geometry <- NULL
}
# Filter by link quality (lqu > 1)
data_good_lqu <- data_duplicates %>%
filter(sigfox_link_quality > 1) %>%
as.data.frame()
#=========================================#
### 3. Convert to Telemetry Object
#=========================================#
# Use cleaned CSV path or saved dataframe
data <- as.telemetry("Kestrel_BP_cleaned.csv")
projection(data) <- median(data)
plot(data)
compass() # North = up
#===================================#
### 4. Outlier Detection & Removal
#===================================#
OUT <- outlie(data, main = "Map Outliers Kestrel_BP")
plot(OUT, main = "Outliers Kestrel_BP")
# Remove speed outliers
MAX_SPEED <- 50
GOOD <- OUT$speed < MAX_SPEED
data_clean <- data[GOOD, ]
OUT_clean <- outlie(data_clean, main = "Speed Outliers Removed Kestrel_BP")
plot(OUT_clean)
# Remove distance outliers
MAX_DIST <- 15000
GOODd <- OUT_clean$distance < MAX_DIST
data_clean <- data_clean[GOODd, ]
OUT_clean <- outlie(data_clean, main = "Distance Outliers Removed Kestrel_BP")
plot(OUT_clean, main = "Outliers removed Kestrel_BP")
#================================================#
### 5. Variogram & Model Fitting
#================================================#
SVF <- variogram(data_clean)
plot(SVF, main = "Variogram Kestrel_BP", xlim = c(0, max(SVF$lag)))
dt.plot(data_clean)
GUESS <- ctmm.guess(data_clean, interactive = FALSE)
FITS <- ctmm.select(data_clean, GUESS, trace = 3, verbose = TRUE, cores = -1)
summary(FITS)
summary(FITS[[1]])
plot(SVF, FITS[[1]], main = "Best-fit Variogram Kestrel_FO", xlim = c(0, max(SVF$lag)))
#================================================#
### 6. Weighted AKDE (Home Range)
#================================================#
wAKDE <- akde(data_clean, FITS[[1]], weights = TRUE)
plot(data_clean, wAKDE, lwd = 2, main = "wAKDE Kestrel_AP")
summary(wAKDE)



