R:当点重叠/在距离内时追加数据;将缓冲区矩形添加到 set1,将半径添加到 set2

Posted

技术标签:

【中文标题】R:当点重叠/在距离内时追加数据;将缓冲区矩形添加到 set1,将半径添加到 set2【英文标题】:R: Append data when points overlap/within distance; add buffer rectangle to set1, add radius to set2 【发布时间】:2019-10-14 10:57:37 【问题描述】:

我有一个涡流数据库 (netcdf),其中包含在海洋中移动的涡流,以及许多鱼的轨迹。当鱼在涡流范围内((变化的)阈值距离内的位置和日期相同)时,我试图将涡流信息附加到鱼道。数据结构、复杂性和可重现的示例如下。我怀疑这不是超级难,我只是不断地打结,试图找出一个巧妙的解决方案。提前感谢您的任何想法!

数据结构/可重现示例:3 天的鱼和涡流轨迹

library(lubridate)
fish <- data.frame(lat = c(42.1, 42.6, 43.2), lon = c(-10, -10.1, -10.2), date = ymd(c("1990-01-01", "1990-01-02", "1990-01-02")))
eddy <- data.frame(lat = c(44, 42.3, 40), lon = c(-15, -10.1, -6), date = ymd(c("1990-01-01", "1990-01-02", "1990-01-02")), track = c(1,1,1), radius = c(81,82,83), vals = c(11,12,13))

这里有点棘手:

    变半径环形涡流缓冲器

涡流的大小(从中计算重叠或距离)由半径列给出,所以:

sf_eddy <- st_as_sf(eddy)
st_buffer(sf_eddy, dist = radius)

对于 st_buffer “所有操作都基于每个功能”,所以我不确定这是否会将 sf_eddy 视为单个功能(可能是(多)线串)或多个单独的点。我想我可以玩弄它,直到我让它工作,这样每个涡流日(n = 151937)都是一个 sf 缓冲圈,我大概可以将其 vals 附加到,如果它们还没有包括在内。

    设置距离 latlong 矩形鱼缓冲区

鱼的位置有一个固定的误差距离,+-1.9°lat,+-0.77°lon。所以从概念上讲,我想在每天的鱼位置周围创建一个又高又细的矩形多边形。 st_buffer 似乎不允许这样做,但 there may be other options 可能会根据 square 示例 here 在每个质心周围创建一个多边形。

    如果鱼和任何涡流在空间和时间上重叠,则将涡流 val 附加到鱼

当 eddy$date==fish$date?虽然涡流计算是一次性的,但我可以保存输出。我怀疑鱼箱计算也不会特别费力。无论如何:

fish$vals <- rep(NA, nrow(fish))    
sf_fish <- sf_as_sf(fish)
for (i in seq(along = sf_fish))
if(st_intersects(sf_fish[i,],sf_eddy)) sf_fish[i,"vals"] <- sf_eddy$vals

显然,这可能是一种糟糕的做法。

但是,这是否是解决这个问题的一种模糊的合理方法,还是由于我对 sf、sp、rgeos 等的业余知识,我错过了更优雅的解决方案?我有 166 个鱼文件,平均 286 天的位置 = 47476 总查找。创建鱼/涡流日期匹配对的索引(同一天可能有多个涡流)然后仅在这些上测试交叉点会好/快吗?由于我需要将eddy$vals(实际上是2或3列)附加到fish,所以使用空间连接会更好,也许st_join

编辑:评论和输出地图怪事: 600,000 条鱼/涡流 ggplot 总输出:

看起来我需要修复投影,因为涡流没有在 0 以西打印。它们现在肯定在同一个 CRS 中;也许原件是 0-360 而不是 -180:180。此外,虽然鱼箱打印得很好,但涡流太小:意味着 r=80km=~1deg@45N,而且它们的打印肯定比这小得多。我也会考虑的。另外:如果同一天有2个涡流,for()会有问题吗?

Edit2:我更正的原件是 0:360。涡流半径仍然太小: 根据代码讨论,带有 latlons 的原始 eddy 文件被转换为 st_as_sf 到 crs4326 (wgs84) 然后 st_transform(sf_eddy,6931) 这是北大西洋,如上图所示。结果文件中的几何列是 POLYGON,XY 尺寸,值是投影坐标而不是纬度,例如sf_eddy_buffered$geometry[1]bbox:xmin -4894790 ymin -2418555 xmax -4894648 ymax -2418413speed_radius 是 71,所以 xmin 和 xmax 之间的差异是 142,即 71*2,这是正确的。半径单位不等于投影距离单位吗?斯图尔特的例子是 81000。涡流半径单位:公里。投影/几何单位:https://epsg.io/6931 表示单位确实是米。将半径乘以 1000 并重试: 所以这一切都很好。刚刚尝试了更新的st_join 代码,认为它可以使用一些工作 - 可能因为 3639209 涡流行与 461 条鱼行而需要永远。它还在同一天为同一条鱼创建了多个涡流的多行。考虑到鱼周围的错误缓冲区框,可能是相对较大的重叠机会的函数。 我怀疑我可以通过标记重复项并删除最远的重复项来解决此问题。当前的执行方式(通过 v 和 rbind)比以前的方式(值的直接单元分配)IMO 更好,因为否则多个值将被发送到单个单元,要么崩溃,要么静默覆盖。

为了加快代码速度,我想我可以将 eddy 文件子集设置为仅当前鱼的日期范围内的重叠部分,并在连接循环中使用该子集。无论如何,连接循环已经按日期对鱼和涡流文件进行了子集化,所以这可能是多余的,或者可能减少需要处理的涡流数据的大小每次都会减少 CPU/RAM 的工作量?

Edit3:这大大加快了速度。为thisDate 添加一个简单的循环计数器显示它在 461 行中的 275 行完成。并制作了一个481长度的文件。可能有很多天鱼不在漩涡中(thisDate 循环导致没有v 和没有rbind),并且有很多天鱼在两个以上的漩涡中(@987654354 @ 循环导致多行 vrbind)。仍然感觉它应该处理 461 循环,但是......

Edit4:一个小调整:

v <- st_join(thisFishRow, thisEddyRow, left = F, largest = TRUE)

largest 表示它只会加入具有最大重叠区域的涡流,导致最大 v 行长为 1,因此每天最多 1 个值。然而,路径的细微差别意味着鱼似乎可以在不同的持续漩涡之间来回穿梭。我的怀疑(基于类似的研究)是它们更有可能留在强大的反气旋涡流系统中。涡流控制的半径(以及面积)可能重叠超过位置。对于 1.5 个月的部分,我发现重叠度最高的涡流轨道编号在 3 个涡流之间反弹。涡道编号 217008 是最强大的反气旋。这是一个(废话)情节: 如您所见,217008 正好位于鱼错误框的中心,而更大、更分散、功率更低的涡流 217343 和 39625 位于边缘。然而,它们较大的尺寸经常会看到它们被撞到顶部,因为它们有更多的区域可以提供,并且不计算质心接近度。 所以:如果 fishbox 在同一天与 eddy 重叠,则将 eddy 包括在候选名单中 (thisFishRow & thisEddyRow 保持不变)。然后:根据与质心的最短距离(而不是st_join)从候选名单中选择涡流。待续!

编辑5:

fishNearEddies <- NULL
for (thisDate in sf_df_nona$date) 
 thisFishRow <- sf_df_nona[sf_df_nona$date == thisDate, ]
 thisEddyRow <- sebDateSub[sebDateSub$date == thisDate, ]
 overlapToday <- st_join(thisFishRow, thisEddyRow, left = F)
 if (nrow(overlapToday) > 0) 
  overlapEddies <- thisEddyRow[thisEddyRow$track %in% overlapToday$track,] 
  st_distance(x = thisFishRow, y = overlapEddies, by_element = TRUE) #0 0 due to overlap min dist = 0
  #close if
 #close for

st_distance 不起作用,因为空间特征(缓冲区和缓冲区圈)重叠,所以最小距离 = 0。我想我还需要质心来测试?

Edit6:使用工作代码进行最终编辑,并为 Stewart 的所有帮助提供答案。再次感谢先生。

# overlap join loop####
fishNearEddies <- NULL
EdCentroidDist <- NULL
counter <- 1
ofhowmany <- length(sf_df_nona$date)
for (thisDate in sf_df_nona$date) 
  thisFishRow <- sf_df_nona[sf_df_nona$date == thisDate, ] #will be 1 row per day
  thisEddyRow <- sebDateSub[sebDateSub$date == thisDate, ] #all eddies that day, could be multi rows
  overlapToday <- st_join(thisFishRow, thisEddyRow, left = F) #will join only if they spatial intersect and (already) time match
  if (nrow(overlapToday) > 0) 
    # now need to join based on closest centroid and NOT on highest overlap
    overlapEddies <- thisEddyRow[thisEddyRow$track %in% overlapToday$track,] #subset TER by overlap tracks
    fishcentroid <- st_centroid(thisFishRow)
    eddycentroid <- st_centroid(overlapEddies)
    fishEdDists <- st_distance(x = fishcentroid, y = eddycentroid, by_element = TRUE) #result vector corresponding to overlapEddies rownumbers
    fishNearEddies <- rbind(fishNearEddies, overlapEddies[which.min(fishEdDists),]) #row index for overlapEddies, no index but has date
    EdCentroidDist <- rbind(EdCentroidDist, as.numeric(min(fishEdDists)))
   #close if
  print(paste0(counter, " of ", ofhowmany, " fish days"))
  counter <- counter + 1
 #close for

if (!is.null(fishNearEddies))   #if there are overlaps, do processing. If not it'll fail

  fishNearEddies %<>% as.data.frame() %>% # convert to nonspatial so I can remove buffer column
    select(-geometry) %>% # remove geometry column. Attributes still remain. Whatever?
    cbind(EdCentroidDist) #add to FNE as column

【问题讨论】:

如何根据要素的质心计算距离(使用st_centroid),然后选择距离最小的要素? 【参考方案1】:

这里有一些东西可以帮助你:

library(lubridate)
library(sf)
library(ggplot2)

# sample data
fish <- data.frame(
    lat = c(41.1, 43.6, 44.2),
    lon = c(-7, -11, -15),
    date = ymd(c("1990-01-01", "1990-01-02", "1990-01-03"))
)

# Add a blank column for the eddy values we want
fish$vals <- rep(NA, nrow(fish))

eddy <- data.frame(
    lat = c(44, 42.3, 40),
    lon = c(-6, -10.1, -15),
    date = ymd(c("1990-01-01", "1990-01-02", "1990-01-03")),
    track = c(1,1,1),
    radius = c(81000,82000,83000),
    vals = c(11,12,13)
)

# Convert eddy to simple features, using WGS84 CRS
sf_eddy <- st_as_sf(eddy,  coords = c("lon", "lat"), crs=4326)

# Transform from geographical to projected so we can buffer correctly. You might need to pick a different CRS
sf_eddy <- st_transform(sf_eddy, 3035) # ETRS89 / LAEA Europe

# Buffer the eddy points based on their radii
sf_eddy_buffered <- st_buffer(sf_eddy, dist = sf_eddy$radius)

# Add error to fish position. There's probably a better way to do this.
fishErrLat <- 1.9
fishErrLon <- 0.77
fish$buffer <- paste('POLYGON((',
                    fish$lon - fishErrLon, ' ', fish$lat + fishErrLat, ',',
                    fish$lon + fishErrLon, ' ', fish$lat + fishErrLat, ',',
                    fish$lon + fishErrLon, ' ', fish$lat - fishErrLat, ',',
                    fish$lon - fishErrLon, ' ', fish$lat - fishErrLat, ',',

                    fish$lon - fishErrLon, ' ', fish$lat + fishErrLat,
                    '))',
                    sep=''
                )

# Convert fish to simple features, using WGS84 CRS
sf_fish <- st_as_sf(fish, wkt='buffer', crs=4326)

# Transform from geographical to projected
sf_fish <- st_transform(sf_fish, 3035)


#Plot what we've got so far    
g <- ggplot() + 
        geom_sf(data=sf_eddy_buffered, aes(fill=date)) +
        geom_sf(data=sf_fish, aes(fill=date))

print(g)

# Check for overlap
fishNearEddies <- NULL

for (thisDate in unique(sf_fish$date)) 

    thisFishRow <- sf_fish[sf_fish$date==thisDate, ]
    thisEddyRow <- sf_eddy_buffered[sf_eddy_buffered$date==thisDate, ]

    v <- st_join(thisFishRow, thisEddyRow, left=F)
    if (nrow(v) > 0) 
        fishNearEddies <- rbind(fishNearEddies, v)
    


并检查结果:

> fishNearEddies
Simple feature collection with 1 feature and 7 fields
geometry type:  POLYGON
dimension:      XY
bbox:           xmin: 2588691 ymin: 2402830 xmax: 2703342 ymax: 2624501
epsg (SRID):    3035
proj4string:    +proj=laea +lat_0=52 +lon_0=10 +x_0=4321000 +y_0=3210000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs
   lat lon     date.x     date.y track radius vals                         buffer
2 43.6 -11 1990-01-02 1990-01-02     1  82000   12 POLYGON ((2644792 2624501, ...

这只会给你那些与涡流相交的鱼。

【讨论】:

是的,for 循环需要进行编辑以处理多个匹配项。如果鱼与两个不同的vals 涡流相匹配,你如何决定选择哪个val 我刚刚注意到您的误差为 ±1.7˚,因此我更新了代码以删除 1.7/2 中的分母。您的数据来自哪种跟踪技术? 我认为你使用sf_join 的想法比我原来使用st_intersectsfor 循环要好。我已经更新了我的答案。 你现在已经超出了我的知识水平。祝你好运! 谢谢斯图尔特;现在上传的答案,可能不是最优雅的,但似乎可以完成这项工作!干杯

以上是关于R:当点重叠/在距离内时追加数据;将缓冲区矩形添加到 set1,将半径添加到 set2的主要内容,如果未能解决你的问题,请参考以下文章

Jaccard 得分/距离或重叠百分比

二维差分前缀和——cf1202D(好题)

点到平面距离公式是啥?

右 | sf:我在每个点周围都有点和 2 个缓冲区。如果较大的缓冲区重叠(但较小的不重叠),如何将这些点组合成单个单元?

使用重叠执行缓冲/窗口添加 CMSampleBufferRef

在opengl中重叠矩形并在底部裁剪