Determine if single GPS coordinate falls within an AKDE UD level

92 views
Skip to first unread message

Robert Heathcote

unread,
Jul 13, 2021, 2:32:54 PM7/13/21
to ctmm R user group
Hi all,

Would really appreciate some help on a slightly odd requirement. Very new to the ctmm package, so please bear with me if this is a stupid question!

I have created akde objects for multiple animals, and for each of them I am now trying to find whether the core home range (85% UD) encompasses a single GPS coordinate.

Can anyone suggest a way of doing this with some example code? Please let me know if you need a reproducible example (I'm not 100% sure if it would help in this instance).

Below is some background (but non vital) info on how I've done this before with regular KDEs if it helps answer the question.

I have done this before (with some pretty ugly code) using the adehabitatHR package by extracting the 85% contour from a UD using the "getverticeshr" function (e.g. getverticeshr(kud,percent=85))
where kud is the UD created from the "kernelUD" function.

I then converted the single gps coordinate (the one that may or may not be encompassed by the home range) into a SpatialPointsDataFrame.

I then ran the following code to determine if the isopleth contained the gps coordinate: over(id,Core_85[,2],fn=function(x) return(1))

Where Core_85 is the 85% contour (i.e. a "SpatialPolygonsDataFrame") from "getverticeshr".

This returns an NA if not found within that kernel, and a 1 if it is found.

Can anyone possibly suggest how I can do something similar in ctmm? I am struggling to even determine what all the slots within the akde object itself are!

Thanks very much in advance, and please let me know if you need more info and a reproducible example.

Best wishes,
Rob

Christen Fleming

unread,
Jul 13, 2021, 2:46:30 PM7/13/21
to ctmm R user group
Hi Rob,

If you export the UD contours to an sp or sf object (https://ctmm-initiative.github.io/ctmm/reference/export.html), then you can use their functions, like sp::point.in.polygon(), for things like this.

If you have to do this for many locations, then it's probably faster to export the UD's CDF to a raster and then extract the cumulative probability at the locations of interest and check if they are <=0.85.

Best,
Chris

Robert Heathcote

unread,
Jul 14, 2021, 12:35:36 PM7/14/21
to ctmm R user group
Hi Chris,

Thanks very much for replying so quickly. My problem is exactly that: I'm not sure (enough) which actual slots within the SpatialPolygonsDataFrame.UD from my akde are actually the contours I would use with the point.in.polygon().

For instance, using your buffalo buffalo$Cilla example, I would run this:

data(buffalo)
Cilla <- buffalo$Cilla
GUESS <- ctmm.guess(Cilla,interactive=FALSE)
FIT <- ctmm.fit(Cilla,GUESS)
UD <- akde(Cilla,FIT)
poly_cilla<-SpatialPolygonsDataFrame.UD(UD,level.UD=0.85)

And then I get stuck trying to figure out which parts of poly_cilla to pass onto the point.in.polygon()!

I'm sorry to be a pain, but is there any chance you could specify with this example how I would then find out if a coordinate (e.g. the first row from buffalo$Cilla: 31.88776, -24.96738) actually falls within this contour using the point.in.polygon() function?

Thanks very much for your help.

Best wishes,
Rob

Christen Fleming

unread,
Jul 14, 2021, 1:48:37 PM7/14/21
to ctmm R user group
Hi Rob,

This is what I got working by just probing into the objects:

names(poly_cilla@polygons) # second list item is point estimate
POLY <- poly_cilla@polygons[[2]]@Polygons[[1]]@coords
sp::point.in.polygon(Cilla$x[1],Cilla$y[1],POLY[,1],POLY[,2])


It seems weird to me that this sp function doesn't make use of sp objects, and requires you to unpack them by hand like this. And the newer sf package doesn't seem to have this function. But I don't know if there is an easier way to do this, because I don't really use sp or sf myself.

This is the alternative approach that I was mentioning, which should be faster and seemed a bit more straightforward to me:

RAS <- raster(UD,DF="CDF")
XY <- data.frame(Cilla[1,c('x','y')])
raster::extract(RAS,XY) <= 0.85


Though you will need to catch the NAs if you leave the grid.

I could implement something like that natively in ctmm if there was a general need for it.

Best,
Chris

Robert Heathcote

unread,
Jul 15, 2021, 9:04:56 AM7/15/21
to ctmm R user group
Chris, you are a hero.

Thank you very much. I won't admit how long I spent trying to solve this before submitting my question, but it turns out that my problem was simply navigating the slots within the spatial polygon (ahem base R skills) and then being confident enough about what I was a actually selecting. This is running very quickly just with the SpatialPolygonsDataFrame.UD function.

Many thanks again. What a fantastic package!

Cheers,
Rob

Reply all
Reply to author
Forward
0 new messages