You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用sf/terra快速计算LINESTRING/MULTILINESTRING的累积长度?

计算LINESTRING/MULTILINESTRING节点累积距离的高效方法

需要计算LINESTRING或MULTILINESTRING从起点到各节点的累积距离,当前方法是拆分线段逐一测量,可行但耗时,求内置/更高效的实现方式。以下是基于spData塞纳河数据集的可复现示例,同时提供terra或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.

期望输出:

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

高效解决方案

1. sf + geos 优化版

避免循环创建线段对象,直接计算相邻点的距离再累加,性能提升显著:

library(sf)
library(geos)
library(spData)

lines <- seine[2, ]

cumulative_length_fast <- function(input) {
  # 获取坐标矩阵
  coords <- st_coordinates(input)
  # 计算相邻点对的距离(自动适配CRS)
  seg_lengths <- geos::geos_distance(
    geos::geos_make_point(coords[-nrow(coords), 1], coords[-nrow(coords), 2], crs = st_crs(input)),
    geos::geos_make_point(coords[-1, 1], coords[-1, 2], crs = st_crs(input))
  )
  # 生成累积距离向量,开头补0
  c(0, cumsum(seg_lengths))
}

# 验证结果
cumulative_length_fast(lines) |> head()
#> [1]    0.000 1716.196 3290.379 4824.087 6745.759 7446.660

# 性能对比
bench::mark(
  original = cumulative_length(lines),
  optimized = cumulative_length_fast(lines)
)

2. terra 解决方案

利用terra的segments()拆分线段,结合distance()计算长度:

library(terra)
library(spData)

# 转换为SpatVector
lines_terra <- vect(seine[2, ])

cumulative_length_terra <- function(input) {
  # 拆分线段为单个片段
  segs <- segments(input)
  # 获取各片段长度
  seg_lengths <- distance(segs, along = TRUE)[, "length"]
  # 生成累积距离向量
  c(0, cumsum(seg_lengths))
}

# 验证结果
cumulative_length_terra(lines_terra) |> head()
#> [1]    0.000 1716.196 3290.379 4824.087 6745.759 7446.660

3. rsgeo 解决方案

rsgeo提供line_segment_lengths()直接获取线段各段长度,无需手动拆分:

library(rsgeo)
library(sf)
library(spData)

lines <- seine[2, ]

cumulative_length_rsgeo <- function(input) {
  # 转换为rsgeo Line对象
  line <- as_rsgeo(input)
  # 获取各线段长度
  seg_lengths <- line_segment_lengths(line)
  # 生成累积距离向量
  c(0, cumsum(seg_lengths))
}

# 验证结果
cumulative_length_rsgeo(lines) |> head()
#> [1]    0.000 1716.196 3290.379 4824.087 6745.759 7446.660

性能说明

上述三种方案均避免了原方法中循环创建大量几何对象的开销,其中sf+geos优化版和rsgeo的实现速度最快,terra方案也远优于原实现。

内容的提问来源于stack exchange,提问作者atsyplenkov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 04:32:32