Calculating distances with inaccessible areas

87 views
Skip to first unread message

Jonathan Rutter

unread,
Jul 31, 2025, 5:18:15 PMJul 31
to ctmm R user group
Hello,

I am new to ctmm, and I would like to use it for an upcoming study of seabird movements (with GPS and PTT). I am interested in birds' pairwise interactions, ie the amount of time (with confidence intervals) they spend within 3 km of each other. The distances() function seems to be the obvious way to do this. But I am concerned about its use for 2 reasons:
  1. These birds never go over land (except at their breeding colony), yet often forage near shore. So, some of the occurrence distributions between GPS or PTT locations will undoubtedly include inaccessible areas. I see that the occurrence() function includes an "SP" argument to account for this, but the distances() function does not. So, my current plan is to somehow identify the time periods during which birds' predicted occurrences intersect with land. Then within those periods, I will calculate bounded occurrence distributions for both individuals at each time step, and calculate the distribution of distances manually with another package (eg terra). Is this a reasonable approach, or is there a way to streamline this within ctmm?
  2. These birds often travel for 1000s of km, meaning a single projected CRS may not be appropriate across an individual's track. I am thus planning to keep everything in lat lon coordinates. I assume distances() is projecting under the hood, so is this something I need to worry about? (I do not care about distances where the low end of the CI is >> 3 km.)
Apologies if I missed the answers to these questions in the forum!

Many thanks,
Jonathan


Christen Fleming

unread,
Aug 4, 2025, 4:40:49 AMAug 4
to ctmm R user group
Hi Jonathan,

2. If they are largely traveling a geodesic over the Earth, like latitudinal migration, then the default 2-point equidistant projection should be fine for the entire dataset, as long as the automated clustering algorithm identifies the two foci and their path doesn't stray too far from geodesic between them.
1. So far there is only support for boundaries at the kernel level and not at the movement-model level, which is something that we have planned for in the next 3 years. You could make predictions at regular times, truncate and renormalize those instantaneous predictions, and then predict distances from those their joint distribution. That's the simplest solution I can think of, but it isn't trivial.

Best,
Chris

Jonathan Rutter

unread,
Aug 8, 2025, 9:29:44 PMAug 8
to Christen Fleming, ctmm R user group
Hi Chris,

Thanks so much for this! Could I clarify the suggestion you had: "You could make predictions at regular times, truncate and renormalize those instantaneous predictions, and then predict distances from those their joint distribution." To predict distances, my current plan is to do this "manually" with the terra package. Is there a more efficient way to do this with ctmm (eg convert truncated rasters back into telemetry objects somehow, then run distances())?

Thanks again,
Jonathan


--
You received this message because you are subscribed to a topic in the Google Groups "ctmm R user group" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/ctmm-user/UDkNl6S3ef8/unsubscribe.
To unsubscribe from this group and all its topics, send an email to ctmm-user+...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/ctmm-user/9c3c2139-ac04-48d0-bb91-e49ffc513849n%40googlegroups.com.

Christen Fleming

unread,
Aug 9, 2025, 9:48:20 PMAug 9
to ctmm R user group
Hi Jonathan,

No, the only other thing you could do within the ctmm package is simulate multiple random trajectories and reject the ones that are invalid, but this would only be computationally feasible if the random paths aren't too likely to venture into inaccessible regions.

Best,
Chris
Message has been deleted

Jonathan Rutter

unread,
Aug 13, 2025, 7:12:48 AMAug 13
to ctmm R user group
Got it, thanks very much!

Jonathan

Jonathan Rutter

unread,
Aug 26, 2025, 12:23:56 PMAug 26
to ctmm R user group
Hi Chris,

I have some follow-up questions on the distance analysis above. I am currently trying what I think is an unconventional approach to using ctmm. I have seabird tracks at a wide range of resolutions (2 min consistent GPS, 30-120 min solar GPS with variations and gaps as battery life fluctuates, and 4-9 hr PTT). As seabird movements are extremely different under different behavioural states, I have used hidden Markov models to classify locations into behavioural states while accounting for how states change at different temporal resolutions. My understanding is that ctmm does not account for behaviour changes within a single track, so I am splitting all of my tracks into behavioural "bouts" - short segments in which movement patterns should be broadly similar. I would like to use a subset of high-quality data (2 min and 30 min GPS) to produce 1 ctmm per behaviour class, averaged across all individuals and segments. Then, under the hood of distances(), I would like to use these ctmms to predict occurrence for all data (including low-quality PTT) across a range of timesteps. My questions are:

1. Does this make sense to you? I hope this is still an appropriate use of ctmm.

2. I am getting stuck on ctmm fitting. My current approach is to use a pooled variogram to estimate GUESS parameters for each behaviour class, then run ctmm.select() for each individual segment, then use mean() to get an average model. However, the average models are not matching up well to the pooled variogram. In some cases they are greatly underestimating both variance and uncertainty (see pooled variogram with average ctmm below). I thought this might relate to the fact that only a few track segments in each class are long enough to accurately estimate sigma and (for OUF) tau_position, but even individual segment ctmms seem to have extremely poor fit. I have already tried using the weights argument in mean(), which helped only a bit. I have thought about using ctmm:::id.parameters to manually constrain tau_position, but this seems risky. Do you have any advice?

Hopefully this makes sense. Many thanks in advance!

Jonathan

Rplot.png

Christen Fleming

unread,
Aug 27, 2025, 2:18:08 PMAug 27
to ctmm R user group
Hi Jonathan,

I think it makes sense to fit one stationary model per behavior, but per segment might not be enough data.

Additionally, when fitting stationary models to slower behaviors, you may need to insert extra time within the gaps so that the individual could have slowly moved from one segment to the next: https://groups.google.com/g/ctmm-user/c/g5UDb40gtUQ/m/2JyshES7BAAJ

Best,
Chris

Jonathan Rutter

unread,
Aug 29, 2025, 9:21:16 AMAug 29
to ctmm R user group
Hi Chris,

Are you suggesting that for each behavioural state, I concatenate the track segments sequentially with a long gap in between each, similar to what you and Thomas were discussing here? In other words, I should end up with 1 long track of 1 behaviour type for 1 bird, with large gaps between segments, where the absolute datetimes are meaningless but the relative datetimes within each segment are correct?

I have had a go at this already but I am running into a computation problem. Bird resting bouts are occasionally hundreds or thousands of km apart. When I expand gaps between segments, I am effectively saying "How long would it take for a bird to float across the ocean to its next resting spot?" The result is that some tracks are decades long, meaning variogram computation time is impractically high. I am now considering spatially translating the segments of 1 behaviour so that they lie end-to-end (potentially with an artificial gap of a few days or so). But this seems very tedious and I fear it could risk some weird autocorrelation artifacts.

So before I get going on this analysis, could I confirm this sounds like a reasonable approach?

Thanks!
Jonathan

Jonathan Rutter

unread,
Aug 31, 2025, 10:55:05 AMAug 31
to ctmm R user group
Hi Chris,

Apologies for the barrage of questions. I've now had a few more attempts at solving the problem above, and some new problems have arisen. Long story short, what I am trying to do is fit one stationary model per behaviour class, with which I can predict occurrences and distances across new individuals. I only care about model fit at short time lags, as I will probably not be computing distances in gaps of >24 hours, and behaviours rarely last for longer than this anyway.

I have considered the following solutions, but each has issues:
  1. I tried concatenating track segments from the same class, with temporal gaps between the segments (described in the previous email). I think I have overcome the computation time problems I described above. But the gap size always seems to have a large effect on model fit, because I cannot constrain ctmm.select() to look at only shorter time lags.
  2. I capped track segment length at 24 hours, which eliminated the asymptote in most variograms. I then successfully fit IOU/BM ctmms to all segments. But when I tried to use mean.ctmm() to average these models, it did not work - it seems that this function only works on range resident models? I have thought about trying to code a custom mean.ctmm() function to work for IOU/BM, but this looks beyond my level of expertise.
  3. I have wondered about fitting OU(F) models to all segments, but the shortness of the segments means the asymptote (sigma) is usually wrong. I have thought about constraining tau_position or sigma with a lower bound, but I don't know if this is possible/advisable.
Am I on the right track with any of these? Any advice would be much appreciated!

Many thanks,
Jonathan

Christen Fleming

unread,
Sep 4, 2025, 2:16:44 PMSep 4
to ctmm R user group
Hi Jonathan,

For plotting the variogram, you could mean() the variograms of each segment.

With the time gap method, the variance of the fitted model would correspond to the broader distribution of segments, but for estimating distances there wouldn't be sensitivity to those broad-scale parameters.

I'll get BM/IOU fits working with mean().

Best,
Chris

Jonathan Rutter

unread,
Sep 5, 2025, 12:33:56 PMSep 5
to ctmm R user group
Hi Chris,

That sounds great, thanks very much! Unless you anticipate this taking longer I'll try using mean() on BM/IOU in a few days.

Best,
Jonathan

Reply all
Reply to author
Forward
0 new messages