【问题标题】:How can I draw a line from a point to a polygon edge and then get the line's length in sf in R?如何从一个点画一条线到多边形边缘,然后在 R 中以 sf 形式获得线的长度?
【发布时间】:2022-02-24 16:35:14
【问题描述】:

还有一些其他帖子相关与此相关,例如:Post 1Post 2Post 3。但是,它们都没有提供我所希望的。我想要的是能够在特定方向(“正南”又名向下)从特定点(采样位置)到完全围绕该点(湖泊边界)的多边形边缘绘制一条线段。然后我想测量采样点和多边形边缘之间的线段的长度(真的,这只是我想要的距离,所以如果我们可以在不绘制线段的情况下获得距离,那就更好了!)。不幸的是,sf 包中似乎不存在执行此操作的功能:See closed issue here

怀疑,不过,这可以通过修改此处提供的解决方案来实现:See copy-pasted code below, modified by me。但是,我对sf 中的工具非常不满意--我只制作了从点本身到多边形南部范围的线段,在某个点与多边形相交:

library(sf)
library(dplyr)

df = data.frame(
  lon = c(119.4, 119.4, 119.4, 119.5, 119.5),
  lat = c(-5.192,-5.192,-5.167,-5.167,-5.191)
)

polygon <- df %>%
  st_as_sf(coords = c("lon", "lat"), crs = 4326) %>%
  summarise(geometry = st_combine(geometry)) %>%
  st_cast("POLYGON")

plot(polygon)

df2 <- data.frame(lon = c(119.45, 119.49, 119.47),
                  lat = c(-5.172,-5.190,-5.183))

points <- df2 %>%
  st_as_sf(coords = c("lon", "lat"), crs = 4326) %>%
  summarise(geometry = st_combine(geometry)) %>%
  st_cast("MULTIPOINT")

plot(points, add = TRUE, col = "red")

# Solution via a loop

xmin <- min(df$lat)

m = list()
# Iterate and create lines
for (i in 1:3) {
  m[[i]] = st_linestring(matrix(
    c(df2[i, "lon"],
      df2[i, "lat"],
      df2[i, "lon"],
      xmin),
    nrow = 2,
    byrow = TRUE
  ))
}
test = st_multilinestring(m)

# Result is line MULTILINESTRING object

plot(test, col = "green", add = TRUE)

但现在我不知道如何使用st_intersection 或任何此类函数来确定交点的位置。我认为,大部分问题在于我创建的不是sf 对象,我不知道如何让它成为一个对象。我假设,如果我能找出线段与多边形相交的位置(或者理想情况下它们相交的最北时间),我可以使用类似st_distance 的函数以某种方式从交点到采样点进行测量。但是,由于湖泊多边形通常真的很复杂,因此一个线段可能会多次与多边形相交(例如,如果在给定点以南有一个半岛),在这种情况下,我想我可以找到每个采样点的“最北”交点并使用该交点,否则为每个采样点取最小的此类距离。

无论如何,如果有人可以告诉我我缺少的几个步骤,那就太好了!我觉得我很近,但又很远......

【问题讨论】:

    标签: r geospatial sf


    【解决方案1】:

    考虑一下这种方法,大致灵感来自我之前关于 lines from points 的帖子

    为了使其更具可重复性,我使用了众所周知且深受喜爱的北卡罗来纳州 shapefile,该文件随 {sf} 和三个半随机 NC 城市的数据框一起提供。

    代码的作用是:

    • 通过 for 循环遍历城市的数据框
    • 创建一条从每个城市开始(“观察”)并在南极结束的线
    • 与解散的北卡罗来纳州相交
    • 将交叉点炸成单独的线串
    • 选择在原点 1 米范围内通过的线串
    • 通过sf::st_lenghth()计算长度
    • 将结果保存为名为@9​​87654328@(结果的缩写:)的{sf}数据框

    我在最终对象中包含了实际的行以使结果更清晰,但您可以选择省略它。

    library(sf)
    library(dplyr)
    library(ggplot2)
    
    shape <- st_read(system.file("shape/nc.shp", package="sf")) %>%  # included with sf package
      summarise() %>% 
      st_transform(4326) # to align CRS with cities
    
    cities <- data.frame(name = c("Raleigh", "Greensboro", "Plymouth"),
                         x = c(-78.633333, -79.819444, -76.747778),
                         y = c(35.766667, 36.08, 35.859722)) %>% 
      st_as_sf(coords = c("x", "y"), crs = 4326)
    
    # a quick overview
    ggplot() +
       geom_sf(data = shape) + # polygon of North Carolina
       geom_sf(data = cities, color = "red") # 3 cities  
    

    # now here's the action!!!
    for (i in seq_along(cities$name)) {
      
      # create a working linestring object
      wrk_line <- st_coordinates(cities[i, ]) %>% 
        rbind(c(0, -90)) %>% 
        st_linestring() %>% 
        st_sfc(crs = 4326) %>% 
        st_intersection(shape) %>% 
        st_cast("LINESTRING") # separate individual segments of multilines
      
      first_segment <- unlist(st_is_within_distance(cities[i, ], wrk_line, dist = 1))  
      
      # a single observation
      line_data <- data.frame(
        name = cities$name[i],
        length = st_length(wrk_line[first_segment]),
        geometry = wrk_line[first_segment]
      )
      
      # bind results rows to a single object
      if (i == 1) {
        res <- line_data
        
      } else {
        res <- dplyr::bind_rows(res, line_data)
        
      } # /if - saving results
      
      
    } # /for
    
    # finalize results
    res <- sf::st_as_sf(res, crs = 4326) 
      
    # result object    
    res
    # Simple feature collection with 3 features and 2 fields
    # Geometry type: LINESTRING
    # Dimension:     XY
    # Bounding box:  xmin: -79.81944 ymin: 33.92945 xmax: -76.74778 ymax: 36.08
    # Geodetic CRS:  WGS 84
    #         name        length                       geometry
    # 1    Raleigh 204289.21 [m] LINESTRING (-78.63333 35.76...
    # 2 Greensboro 141552.67 [m] LINESTRING (-79.81944 36.08...
    # 3   Plymouth  48114.32 [m] LINESTRING (-76.74778 35.85...
    
    # a quick overview of the lines
    ggplot() +
      geom_sf(data = shape) + # polygon of North Carolina
      geom_sf(data = res, color = "red") # 3 lines
    

    【讨论】:

    • 非常好的答案!感谢分享……以及幽默感!总是很高兴阅读您的帖子。干杯
    • 嗨@Jindra Lacko——一个非常好的答案!出于好奇,如果其中一个点位于该州的东侧,边界非常不规则并且线段可能与多边形多次相交,会发生什么情况?知道如何只选择这些交叉点的​​“第一个”(从北部)会很有用,因为在我的工作案例中,这将经常发生在湖泊多边形中。干杯!
    • 显然,这就是发生的事情——我在cities 分配中输入北卡罗来纳州萨福克的坐标来得到这个:imgur.com/a/Sb7ExnV。然后,您可以使用st_length(st_cast(res, "LINESTRING")) 来取回较小段的长度(尽管似乎不是逻辑顺序?)。也许有一个更优雅的方式......你能澄清一下:1)你能把 any 点放在rbind() 调用这里来改变方向/方位吗?和 2) st_intersection() 实际上在这里做什么?我认为解决这两个问题会进一步改善您的答案(尽管它已经很棒了)!
    • @Bajcz 我已根据您的 cmets 更新了答案;我添加了一段代码,将线和“湖”的交集从单个多线串炸成多个线串,然后为包含原点的线选择距离和几何形状(或者更确切地说,距离原点 1 米以内,因为浮点数学)
    • 如果有人通过谷歌找到这篇文章并想计算湖多边形的获取,我已经成功地修改了这个奇妙答案中的代码,以便为湖中的任何点执行此操作。关键是rbind 调用——将调用中的第一个值替换为介于 -180 和 180 之间的值序列,以将线段旋转 360 度,然后找到产生最大长度的最长相对线段配对。太棒了!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-21
    • 2018-08-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多