Calculate distance between buffer and geographic points for grouped subsets (and get tally)

The data I used can be found here.

After importing my data, I select a subset of points (Nest2017 in column type) to create 30 m buffers around:

library(dplyr)

nests<-df %>%
filter(type %in% "Nest2017")

library(maptools)
library(sp)
library(rgeos)

nests\$lon <- as.numeric(as.character(nests\$lon))
nests\$lat <- as.numeric(as.character(nests\$lat))
coordinates(nests) <- c("lon", "lat")

proj4string(nests) <- CRS("+init=epsg:3578") #setting the projection of the points

spTransform(nests,CRS("+init=epsg:3578")) #tranforming the coordinates

gBuffer(nests, width=30, byid=TRUE) #puts a circular buffer around each individual point

I know gBuffer()requires the points in UTM, but my points go across two zones (7 and 8), so I used the Yukon Albers projection instead.

Now I am trying to figure out how to calculate the distance from the buffer around each Nest2017 (in column type) to each point (Foray) within the same NatalMidden group.

A subset of my data looks as follows:

lat                  lon                NatalMidden   squirrelID    type
60.9577819984406    -138.0347849708050  -27           NA            Nest2017
60.9574120212346    -138.0345689691600  -27           NA            NatalMidden
60.9578209742904    -138.0346520338210  -27           23054         Foray
60.9575380012393    -138.0348329991100  -27           23054         Foray
60.9576250053942    -138.0339069664480  -27           23054         Foray
60.957643026486     -138.0338829942050  -27           23054         Foray
60.9575670026243    -138.0348739866170  -27           23054         Foray
60.9600000176579    -138.032592013478   -515          22780         Foray
60.9600180387497    -138.032631995156   -515          22780         Foray
60.9599519893527    -138.032342987135   -515          NA            NatalMidden
60.959974033758     -138.032317003235   -515          NA            Nest2017

So, for example, squirrelID 23054 was located (Foray) multiple times (type column) and I have a corresponding latitude (lat) and longitude (lon) for each Foray. I am trying to calculate the distance between each Foray (type column) and the 30 m buffer (assuming I correctly created the buffers) around its corresponding Nest2017 (type column) for each individual (squirrelID) separately. The common field that links squirrelID and type is the NatalMidden column.

Is there a way I could work within the dplyr framework to group_by(squirrelID) and then calculate the distances between (and tally) each Foray and its corresponding Nest2017 30 meter buffer (the common field being NatalMidden for both the Foray and Nest2017)? If not, how might I be able to do this?

My ultimate goal is to create new columns for:

1. distance between each Foray and its corresponding 30 meter buffer (centered on Nest2017) for each squirrelID
2. number of Forays inside corresponding 30 meter buffer (centered on Nest2017) for each squirrelID
3. number of Forays outside corresponding 30 meter buffer (centered on Nest2017) for each squirrelID
• You might consider splitting the data into two data frames - Forays and Nests, then join on the NatalMidden value so each row has two geometries, then compute the row-wise distance. You don't need dplyr for any of this. – Spacedman Jan 11 at 19:57
• If your 30m buffers are around single points and so are circles with a 30m radius then you don't need to compute them as a geometry - you can compute point-to-point distances and anything less than 30m would be inside the "buffer" (and have a distance-to-buffer of 0m) and anything > 30m from the point would have a distance-to-buffer of point-distance minus 30m. – Spacedman Jan 11 at 20:23

Read the data, convert to sf, set the CRS, transform to the new (metric) CRS and plot it:

> df = st_as_sf(df, coords=c("lon","lat"))
> st_crs(df)=4326
> df = st_transform(df, 3578)
> plot(df\$geometry,col=df\$type,pch=19)

Now split it into forays and nests - we only need the Natal Midden column for the nests (plus the geometry which we get for free anyway because these are sf spatial data frames).

> Forays = df[df\$type=="Foray",]
> Nests = df[df\$type=="Nest2017","NatalMidden"]

Now join the Forays to the Nests via the NatalMidden column. I'll do this with dplyr but merge could do it too:

> Forays = dplyr::left_join(Forays, data.frame(Nests), c("NatalMidden"="NatalMidden"))
Simple feature collection with 6 features and 3 fields
Active geometry column: geometry.x
geometry type:  POINT
dimension:      XY
bbox:           xmin: 200427.2 ymin: 731035 xmax: 200451.7 ymax: 731043.8
epsg (SRID):    3578
proj4string:    +proj=aea +lat_1=61.66666666666666 +lat_2=68 +lat_0=59 +lon_0=-132.5 +x_0=500000 +y_0=500000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs
NatalMidden sq_id  type                geometry.x                geometry.y
1        -515 22780 Foray   POINT (200427.3 731035) POINT (200465.3 731052.2)
2        -515 22780 Foray POINT (200446.1 731041.4) POINT (200465.3 731052.2)
3        -515 22780 Foray POINT (200450.9 731043.5) POINT (200465.3 731052.2)
4        -515 22780 Foray POINT (200451.7 731043.8) POINT (200465.3 731052.2)
5        -515 22780 Foray POINT (200448.5 731042.7) POINT (200465.3 731052.2)
6        -515 22780 Foray   POINT (200427.2 731035) POINT (200465.3 731052.2)
geometry
1 GEOMETRYCOLLECTION EMPTY
2 GEOMETRYCOLLECTION EMPTY
3 GEOMETRYCOLLECTION EMPTY
4 GEOMETRYCOLLECTION EMPTY
5 GEOMETRYCOLLECTION EMPTY
6 GEOMETRYCOLLECTION EMPTY

Now each row has a geometry from Forays, a geometry from Nests, and an empty geometry because I don't know but its not important. Lets zap that column and then compute the element-wise distance from each Foray point to its Nest point:

> Forays\$geometry = NULL
> Forays\$dist = st_distance(Forays\$geometry.x, Forays\$geometry.y, by_element=TRUE)
Simple feature collection with 6 features and 4 fields
Active geometry column: geometry.x
geometry type:  POINT
dimension:      XY
bbox:           xmin: 200427.2 ymin: 731035 xmax: 200451.7 ymax: 731043.8
epsg (SRID):    3578
proj4string:    +proj=aea +lat_1=61.66666666666666 +lat_2=68 +lat_0=59 +lon_0=-132.5 +x_0=500000 +y_0=500000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs
NatalMidden sq_id  type                geometry.x                geometry.y
1        -515 22780 Foray   POINT (200427.3 731035) POINT (200465.3 731052.2)
2        -515 22780 Foray POINT (200446.1 731041.4) POINT (200465.3 731052.2)
3        -515 22780 Foray POINT (200450.9 731043.5) POINT (200465.3 731052.2)
4        -515 22780 Foray POINT (200451.7 731043.8) POINT (200465.3 731052.2)
5        -515 22780 Foray POINT (200448.5 731042.7) POINT (200465.3 731052.2)
6        -515 22780 Foray   POINT (200427.2 731035) POINT (200465.3 731052.2)
dist
1 41.69695 [m]
2 21.97295 [m]
3 16.79233 [m]
4 15.91557 [m]
5 19.27717 [m]
6 41.79212 [m]

That's probably the meat of the problem. The rest is just aggregate/summarise operations grouped over sq_id. Test dist against 30 metres to see if the point is in the "buffer" or not.

• My major problem was with calculating those distances as a first step, so thank you for explaining that in such detail. With those figured out, I can then group_by and calculate those other values. Thank you! – Blundering Ecologist Jan 12 at 22:16