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

如何使用sf包绘制连接每个点与其最近两个邻点的连线?

用sf包实现点与最近两个邻点的连线

问题背景

现有一组10个点的sf格式空间数据,数据定义如下:

library(tidyverse)
library(sf)

df.sf <- structure(list(component_number = c(51, 51, 51, 51, 51, 51, 51, 
51, 51, 51), geometry = structure(list(structure(c(2529693.76455455, 
437803.242940758), class = c("XY", "POINT", "sfg")), structure(c(2528862.86355918, 
436123.858325239), class = c("XY", "POINT", "sfg")), structure(c(2528991.21479502, 
436854.889372002), class = c("XY", "POINT", "sfg")), structure(c(2529138.56071318, 
436573.087631819), class = c("XY", "POINT", "sfg")), structure(c(2529133.32326354, 
436834.480073507), class = c("XY", "POINT", "sfg")), structure(c(2529133.70746582, 
437094.447431574), class = c("XY", "POINT", "sfg")), structure(c(2529134.07395407, 
437354.456933641), class = c("XY", "POINT", "sfg")), structure(c(2529193.24413696, 
437824.056422966), class = c("XY", "POINT", "sfg")), structure(c(2529456.32924802, 
437147.290262866), class = c("XY", "POINT", "sfg")), structure(c(2529456.32924802, 
437147.290262866), class = c("XY", "POINT", "sfg"))), class = c("sfc_POINT", 
"sfc"), precision = 0, bbox = structure(c(xmin = 2528862.86355918, 
ymin = 436123.858325239, xmax = 2529693.76455455, ymax = 437824.056422966
), class = "bbox"), crs = structure(list(input = "NAD27 / Wisconsin South", 
    wkt = "PROJCRS[\"NAD27 / Wisconsin South\",\n    BASEGEOGCRS[\"NAD27\",\n        DATUM[\"North American Datum 1927\",\n            ELLIPSOID[\"Clarke 1866\",6378206.4,294.978698213898,\n                LENGTHUNIT[\"metre\",1]]],\n        PRIMEM[\"Greenwich\",0,\n            ANGLEUNIT[\"degree\",0.0174532925199433]],\n        ID[\"EPSG\",4267]],\n    CONVERSION[\"Wisconsin CS27 South zone\",\n        METHOD[\"Lambert Conic Conformal (2SP)\",\n            ID[\"EPSG\",9802]],\n        PARAMETER[\"Latitude of false origin\",42,\n            ANGLEUNIT[\"degree\",0.0174532925199433],\n            ID[\"EPSG\",8821]],\n        PARAMETER[\"Longitude of false origin\",-90,\n            ANGLEUNIT[\"degree\",0.0174532925199433],\n            ID[\"EPSG\",8822]],\n        PARAMETER[\"Latitude of 1st standard parallel\",42.7333333333333,\n            ANGLEUNIT[\"degree\",0.0174532925199433],\n            ID[\"EPSG\",8823]],\n        PARAMETER[\"Latitude of 2nd standard parallel\",44.0666666666667,\n            ANGLEUNIT[\"degree\",0.0174532925199433],\n            ID[\"EPSG\",8824]],\n        PARAMETER[\"Easting at false origin\",2000000,\n            LENGTHUNIT[\"US survey foot\",0.304800609601219],\n            ID[\"EPSG\",8826]],\n        PARAMETER[\"Northing at false origin\",0,\n            LENGTHUNIT[\"US survey foot\",0.304800609601219],\n            ID[\"EPSG\",8827]]],\n    CS[Cartesian,2],\n        AXIS[\"easting (X)\",east,\n            ORDER[1],\n            LENGTHUNIT[\"US survey foot\",0.304800609601219]],\n        AXIS[\"northing (Y)\",north,\n            ORDER[2],\n            LENGTHUNIT[\"US survey foot\",0.304800609601219]],\n    USAGE[\n        SCOPE[\"Engineering survey, topographic mapping.\"],\n        AREA[\"United States (USA) - Wisconsin - counties of Adams; Calumet; Columbia; Crawford; Dane; Dodge; Fond Du Lac; Grant; Green; Green Lake; Iowa; Jefferson; Juneau; Kenosha; La Crosse; Lafayette; Manitowoc; Marquette; Milwaukee; Monroe; Ozaukee; Racine; Richland; Rock; Sauk; Sheboygan; Vernon; Walworth; Washington; Waukesha; Waushara; Winnebago.\"],\n        BBOX[42.48,-91.43,44.33,-86.95]],\n    ID[\"EPSG\",32054]]"), class = "crs"), n_empty = 0L)), row.names = c(NA, 
-10L), sf_column = "geometry", agr = structure(c(component_number = NA_integer_), levels = c("constant", 
"aggregate", "identity"), class = "factor"), class = c("sf", 
"tbl_df", "tbl", "data.frame"))

执行plot(df.sf)可查看点的分布,需求是:用sf包绘制连线,将每个点与其最近的两个邻点连接。

解决方案

步骤1:计算距离矩阵并筛选最近邻点

首先计算所有点之间的平面距离矩阵,为每个点筛选出距离最近的两个邻点(排除自身):

# 计算点之间的距离矩阵
dist_matrix <- st_distance(df.sf)

# 为每个点提取最近的2个邻点(第1位是自身距离0,取第2、3位)
nearest_neighbors <- apply(dist_matrix, 1, function(x) order(x)[2:3])

# 整理为点对数据框
point_pairs <- data.frame(
  from = rep(1:nrow(df.sf), each = 2),
  to = as.vector(nearest_neighbors)
)

步骤2:生成连线的sf线段数据

基于点对构造LINESTRING类型的sf对象,同时去除重复线段(比如A→B和B→A属于同一条线,仅保留一次):

# 逐个构造线段
lines <- apply(point_pairs, 1, function(pair) {
  st_sfc(
    st_linestring(c(st_coordinates(df.sf[pair[1], ]), st_coordinates(df.sf[pair[2], ]))),
    crs = st_crs(df.sf)
  )
})

# 转换为sf对象
lines_sf <- st_sf(geometry = do.call(c, lines))

# 去除重复线段:通过排序坐标字符串判断重复
lines_sf <- lines_sf %>%
  mutate(
    coord_key = map_chr(geometry, function(line) {
      coords <- st_coordinates(line)
      # 对线段的两个端点坐标排序后生成唯一标识
      sorted_coords <- coords[order(coords[,1], coords[,2]), ]
      paste(sapply(1:nrow(sorted_coords), function(i) paste(sorted_coords[i, ], collapse = ",")), collapse = ";")
    })
  ) %>%
  distinct(coord_key, .keep_all = TRUE) %>%
  select(-coord_key)

步骤3:绘制结果

将原始点和连线叠加绘制:

# 绘制红色点
plot(st_geometry(df.sf), pch = 16, col = "red", cex = 1.5, main = "点与最近两个邻点的连线")
# 叠加蓝色连线
plot(st_geometry(lines_sf), col = "blue", lwd = 1.5, add = TRUE)

说明

  • 示例中存在重复点(第9、10个点),处理逻辑会自动过滤重复的连线。
  • 距离计算采用数据自带坐标系(NAD27/Wisconsin South)下的平面距离,结果符合投影坐标系的度量规则。

内容的提问来源于stack exchange,提问作者John J.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 17:35:56