有没有更有效的方法来测量<LINESTRING><MULTILINESTRING>的累积长度?换句话说,我需要测量从起点到终点的点之间的距离.目前,我只想出了一个 idea ,将这<LINESTRING>个部分分成单独的部分,并测量每一个部分的长度.这是可行的,但由于迭代方法,它需要花费大量时间.是否存在任何内置方法?

下面是我想出来的一份复印件.它以公元spData年的塞纳河为例.尽管下面的示例使用了sfgeos包,但我很高兴听到像terrageos甚至rsgeo这样的其他空间包.

干杯!

library(sf)
#> Linking to GEOS 3.10.2, GDAL 3.4.1, PROJ 8.2.1; sf_use_s2() is TRUE
library(geos)
library(spData)
#> To access larger datasets in this package, install the spDataLarge
#> package with: `install.packages('spDataLarge',
#> repos='https://nowosad.github.io/drat/', type='source')`

# Example
lines <- seine[2, ]

# My foo
cumulative_length <- 
  function(input) {
    
    # Save CRS
    crs <- sf::st_crs(input)
    
    # Retrive coordinates
    lines_coo <- 
      sf::st_coordinates(input)
    
    # Count number of segments of linestring
    n <- nrow(lines_coo) - 1
    
    # Pre-allocate a list 
    lines_geos <- 
      vector(mode = "list", length = n)
    
    # Construct linestrings
    for (i in 1:n) {
      lines_geos[[i]] <- 
        geos::geos_make_linestring(lines_coo[i:(i+1),1], 
                                   lines_coo[i:(i+1),2], 
                                   crs = crs)
    }
    
    # Measure cumulative segment length
    lines_order <- 
      sapply(lines_geos, geos::geos_length) |> 
      append(0, 0) |> 
      cumsum()
    
    return(lines_order)
    
  }

bench::mark(cumulative_length(lines))
#> # A tibble: 1 × 6
#>   expression                    min   median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr>               <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#> 1 cumulative_length(lines)     13ms   13.7ms      72.9     244KB     109.

创建于2024-03-23,共reprex v2.0.2

UPDATE

期望的输出正好如下所示,即长度从0st_length(lines)的单调递增的数字向量:

cumulative_length(lines) |> 
  head()
#> [1]    0.000 1716.196 3290.379 4824.087 6745.759 7446.660

推荐答案

分析你的功能,大部分时间是geos_make_linestring.因此,假设您可以将数据转换为平面坐标(UTM),则可以直接计算两点之间的距离来跳过该操作:

library(sf)
library(geos)
library(spData)
# Example
input <- seine[2, ] |> 
  st_transform(23032)

# My foo
cumulative_length <- function(input) {
  
  # Save CRS
  crs <- sf::st_crs(input)
  
  # Retrive coordinates
  lines_coo <- 
    sf::st_coordinates(input)
  
  # Count number of segments of linestring
  n <- nrow(lines_coo) - 1
  
  # Pre-allocate a list 
  lines_geos <- 
    vector(mode = "list", length = n)
  
  # Construct linestrings
  for (i in 1:n) {
    lines_geos[[i]] <- 
      geos::geos_make_linestring(lines_coo[i:(i+1),1], 
                                 lines_coo[i:(i+1),2], 
                                 crs = crs)
  }
  
  # Measure cumulative segment length
  lines_order <- 
    sapply(lines_geos, geos::geos_length) |> 
    append(0, 0) |> 
    cumsum()
  
  return(lines_order)
}

cartesian_dist <- function(x1, y1, x2, y2) {
  # √[(x2 − x1)2 + (y2 − y1)2]
  xmax <- max(x1, x2)
  xmin <- min(x1, x2)
  ymax <- max(y1, y2)
  ymin <- min(y1, y2)
  sqrt((xmax - xmin)^2 + (ymax - ymin)^2)
}

cumulative_length2 <- function(input) {
    
    # Retrive coordinates
    lines_coo <- sf::st_coordinates(input)
    
    # Count number of segments of linestring
    n <- nrow(lines_coo) - 1
    
    # Pre-allocate a list 
    dists <- numeric(n)
    
    # Construct linestrings
    for (i in 1:n) {
      dists[[i]] <- cartesian_dist(lines_coo[i, 1], lines_coo[i, 2], lines_coo[i+1, 1], lines_coo[i+1, 2])
    }
    
    # Measure cumulative segment length
    c(0, cumsum(dists))
    
}

microbenchmark::microbenchmark(
  cumulative_length(input),
  cumulative_length2(input),
  times = 100
)
#> Unit: milliseconds
#>                       expr     min      lq      mean   median       uq     max
#>   cumulative_length(input) 22.6733 23.5284 27.012571 24.42025 28.93890 53.6292
#>  cumulative_length2(input)  2.5730  2.7048  3.484514  2.82825  2.98925 21.8944
#>  neval
#>    100
#>    100

创建于2024—03—23,reprex v2.1.0

如果无法转换为平面坐标,也可以使用WGS84投影并使用哈弗正弦公式计算点之间的距离,而不是笛卡尔距离:

haversine_dist <- function(lon1, lat1, lon2, lat2){
  R <- 6371e3 # metres
  phi1 <- lat1 * pi/180 # radians
  phi2 = lat2 * pi/180 # radians
  delta_phi = (lat2-lat1) * pi/180 # radians
  delta_lambda = (lon2-lon1) * pi/180
  
  a <- sin(delta_phi/2) * sin(delta_phi/2) + cos(phi1) * cos(phi2) * sin(delta_lambda/2) * sin(delta_lambda/2)
  c <- 2 * atan2(sqrt(a), sqrt(1-a))
  
  R * c
}

R相关问答推荐

在ggplot Likert条中添加水平线

如何使用rmarkdown和kableExtra删除包含折叠行的表的第一列的名称

编辑文件后编辑RhandsonTable

在"gt"表中添加第二个"groupname_col",而不连接列值

如何在kableextra调用cell_spec()中忽略NA?

使用sf或terra的LINESTRAING的累积长度

R函数,用于生成伪随机二进制序列,其中同一数字在一行中不出现超过两次

为什么我使用geom_density的绘图不能到达x轴?

正在导出默认的RStudio主题,还是设置括号 colored颜色 ?

从数据创建数字的命名列表.R中的框

如何在反曲线图中更改X标签

防止正则表达式覆盖以前的语句

如何为混合模型输出绘制不同的线型?

名字的模糊匹配

如何在R中创建这些列?

如何创建直方图与对齐的每月箱?

合并多个数据帧,同时将它们的名称保留为列名?

通过不完全重叠的多个柱连接

如果缺少时间,如何向日期-时间列添加时间

具有某些列的唯一值的数据帧