计算R中两个SF点之间的角度以计算路线线的方向

发布于 2025-01-29 13:04:55 字数 1729 浏览 4 评论 0 原文

试图使用每个SF Linestring的起点和终点来计算线条载体的SF矢量角度。我不想将阵容分开。

我在此类似示例中使用了该公式进行学位计算 计算两个纬度/经度点之间的

角度网络,创建一个函数“角度”,该函数将列添加到数据框架中,然后将其绘制在MapView中,因此每条线都按角度颜色。但是,值不正确。我认为公式的简单错误?

library(osmdata)
library(sf)
library(dplyr)


bb <- st_bbox(c(xmin = 5.22, xmax = 5.246, ymax = 52.237, ymin = 52.227), crs = st_crs(4326))

x <- opq(bbox = bb) %>%
  add_osm_feature(key = c('highway')) %>%
  osmdata_sf()

##extract building polygons
roads <- x$osm_lines %>% 
  filter(highway == "motorway") %>% 
  select(osm_id, geometry)

angles <- function(x){
  
  ## define line finish coordinates
  f <- x %>%
    st_transform(28992) %>% 
    st_line_sample(sample = 1) %>%
    st_cast("POINT") %>% 
    st_transform(4326) %>% 
    st_coordinates()
  
  ## define line start coordinates
  s <- x %>%
    st_transform(28992) %>% 
    st_line_sample(sample = 0) %>% 
    st_cast("POINT") %>% 
    st_transform(4326) %>% 
    st_coordinates()
  
  ## get latitude of finish points
  lat2 <- f[,2]
  ## get latitudes of start points
  lat1 <- s[,2]
  

  
  ## get longitudes of start points
  lon2 <- f[,1]
  ## get longitudes of start points
  lon1 <- s[,1]
  
  ## delta longitudes
  #dlon <- f[,1]-s[,1]
  #theta <- (atan2(sin(dlon)*cos(lat2), cos(lat1)*sin(lat2)-sin(lat1)*cos(lat2)*cos(dlon)))*180/pi
  
  theta <- atan2(lat2-lat1, lon2-lon1)*180/pi
  
  x$angle_deg <- theta
  
  return(x)
  
}

a <- angles(roads) %>% 
  select(angle_deg)

library(mapview)

mapview(a)

Trying to calculate the angle of an sf vector of linestrings using the start and end points of each sf linestring. I don't want to split the line up.

I used the formula in this similar example for the degrees calculation
Calculate angle between two Latitude/Longitude points

Example below downloads a small road network, creates a function 'angles' which adds a column to the data frame with the angle and then plots it in mapview so each line is coloured by angle. However, the values are not correct. I think simple error with the formula?

library(osmdata)
library(sf)
library(dplyr)


bb <- st_bbox(c(xmin = 5.22, xmax = 5.246, ymax = 52.237, ymin = 52.227), crs = st_crs(4326))

x <- opq(bbox = bb) %>%
  add_osm_feature(key = c('highway')) %>%
  osmdata_sf()

##extract building polygons
roads <- x$osm_lines %>% 
  filter(highway == "motorway") %>% 
  select(osm_id, geometry)

angles <- function(x){
  
  ## define line finish coordinates
  f <- x %>%
    st_transform(28992) %>% 
    st_line_sample(sample = 1) %>%
    st_cast("POINT") %>% 
    st_transform(4326) %>% 
    st_coordinates()
  
  ## define line start coordinates
  s <- x %>%
    st_transform(28992) %>% 
    st_line_sample(sample = 0) %>% 
    st_cast("POINT") %>% 
    st_transform(4326) %>% 
    st_coordinates()
  
  ## get latitude of finish points
  lat2 <- f[,2]
  ## get latitudes of start points
  lat1 <- s[,2]
  

  
  ## get longitudes of start points
  lon2 <- f[,1]
  ## get longitudes of start points
  lon1 <- s[,1]
  
  ## delta longitudes
  #dlon <- f[,1]-s[,1]
  #theta <- (atan2(sin(dlon)*cos(lat2), cos(lat1)*sin(lat2)-sin(lat1)*cos(lat2)*cos(dlon)))*180/pi
  
  theta <- atan2(lat2-lat1, lon2-lon1)*180/pi
  
  x$angle_deg <- theta
  
  return(x)
  
}

a <- angles(roads) %>% 
  select(angle_deg)

library(mapview)

mapview(a)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

迷爱 2025-02-05 13:04:55

我建议您考虑 lwgeom :: st_geod_azimuth() - 它将给出轴承(以弧度为单位)。

要考虑的两件事:

  • 它需要几何类型 - 因为从一个点到另一个点是清晰的,但是 linestring的斜率 may/ther将更改一个部分
  • 它将给出一个元素的向量小于原始点 - 最后一点没有定义的轴承,因为没有“下一个”点;为了解决这个问题,我将人造 na 添加到最后一个元素。

如果您希望以学位的输出,只需将计算输出到a units :: set_units :: set_units :: set_units(“ demrees”) 呼叫。

library(osmdata)
library(sf)
library(dplyr)


bb <- st_bbox(c(xmin = 5.22, xmax = 5.246, ymax = 52.237, ymin = 52.227), crs = st_crs(4326))

x <- opq(bbox = bb) %>%
  add_osm_feature(key = c('highway')) %>%
  osmdata_sf()

##extract building polygons
roads <- x$osm_lines %>% 
  filter(highway == "motorway") %>% 
  select(osm_id, geometry)  %>% 
  st_cast("POINT") %>% 
  mutate(bearing = c(lwgeom::st_geod_azimuth(.),
                     units::set_units(NA, "radians"))) # 1 extra point align vector length

mapview::mapview(roads["bearing"])

I suggest you consider lwgeom::st_geod_azimuth() - it will give bearing (in radians).

Two things to consider:

  • it requires geometry type POINT - because bearing from one point to another is crystal clear, but slope of a LINESTRING may / will vary one section to the next
  • it will give a vector of one element less than the original points - the last point has no defined bearing, as there is no "next" point; to resolve this I am adding artificial NA to the last element.

Should you desire output in degrees just pipe the calculation to a units::set_units("degrees") call.

library(osmdata)
library(sf)
library(dplyr)


bb <- st_bbox(c(xmin = 5.22, xmax = 5.246, ymax = 52.237, ymin = 52.227), crs = st_crs(4326))

x <- opq(bbox = bb) %>%
  add_osm_feature(key = c('highway')) %>%
  osmdata_sf()

##extract building polygons
roads <- x$osm_lines %>% 
  filter(highway == "motorway") %>% 
  select(osm_id, geometry)  %>% 
  st_cast("POINT") %>% 
  mutate(bearing = c(lwgeom::st_geod_azimuth(.),
                     units::set_units(NA, "radians"))) # 1 extra point align vector length

mapview::mapview(roads["bearing"])

enter image description here

蓝眼睛不忧郁 2025-02-05 13:04:55

非常感谢。我意识到也许我对这个问题并不完全清楚。我不想在使用角度将其与其他几何形状的数据填充这些线时,不想将线索分开。但是,您的代码对此有所帮助。这是我所拥有的最终代码,可以使您适应每个唯一的Linestring角度。

library(osmdata)
library(sf)
library(dplyr)

    bb <- st_bbox(c(xmin = 5.22, xmax = 5.246, ymax = 52.237, ymin = 52.227), crs = st_crs(4326))
    
    x <- opq(bbox = bb) %>%
      add_osm_feature(key = c('highway')) %>%
      osmdata_sf()
    
pts <- x$osm_lines %>% ## extract polylines from osm data
  filter(highway == "motorway") %>% ## only motorway network
  select(osm_id, geometry)  %>% ## remove excess columns
  st_cast("POINT") %>% ## split into constituent points
  group_by(osm_id) %>% 
  filter(row_number()==1 | row_number()==n()) %>% ## take start and end points of each osm id
  mutate(bearing = c(lwgeom::st_geod_azimuth(geometry))) %>% ## calculate bearing of start and end points
  mutate(bearing = units::set_units(bearing, "degrees")) %>% ## convert to degrees
  distinct(osm_id, .keep_all = TRUE) ## just take one point for each osm id
    
    roads <- x$osm_lines %>% 
      filter(highway == "motorway") %>% 
      select(osm_id, geometry)  %>% 
      mutate(bearing = pts$bearing)
    
    mapview::mapview(roads["bearing"])

Many thanks for this. I realise maybe I wasn't completely clear in the question. I don't want to split the linestrings up as I am using the angle to populate these lines with data from other geometry. However, your code helped a lot. Here is the final code I have which adapts yours to result in angle of each unique linestring.

library(osmdata)
library(sf)
library(dplyr)

    bb <- st_bbox(c(xmin = 5.22, xmax = 5.246, ymax = 52.237, ymin = 52.227), crs = st_crs(4326))
    
    x <- opq(bbox = bb) %>%
      add_osm_feature(key = c('highway')) %>%
      osmdata_sf()
    
pts <- x$osm_lines %>% ## extract polylines from osm data
  filter(highway == "motorway") %>% ## only motorway network
  select(osm_id, geometry)  %>% ## remove excess columns
  st_cast("POINT") %>% ## split into constituent points
  group_by(osm_id) %>% 
  filter(row_number()==1 | row_number()==n()) %>% ## take start and end points of each osm id
  mutate(bearing = c(lwgeom::st_geod_azimuth(geometry))) %>% ## calculate bearing of start and end points
  mutate(bearing = units::set_units(bearing, "degrees")) %>% ## convert to degrees
  distinct(osm_id, .keep_all = TRUE) ## just take one point for each osm id
    
    roads <- x$osm_lines %>% 
      filter(highway == "motorway") %>% 
      select(osm_id, geometry)  %>% 
      mutate(bearing = pts$bearing)
    
    mapview::mapview(roads["bearing"])

唱一曲作罢 2025-02-05 13:04:55

您可能有兴趣使用空间网络的 sfnetworks 软件包。它包括函数 edge_azimuth()来计算网络中边缘的方位角。

请注意,OSM数据可能需要进行一些预处理,然后才能形成一个干净的可路由网络。参见有关预处理和清洁的sfnetworks小插图:

preprex:

library(osmdata)
#> Data (c) OpenStreetMap contributors, ODbL 1.0. https://www.openstreetmap.org/copyright
library(sf)
#> Linking to GEOS 3.10.1, GDAL 3.4.0, PROJ 8.2.0; sf_use_s2() is TRUE
library(dplyr)
library(sfnetworks)

bb <- st_bbox(c(xmin = 5.22, xmax = 5.246, ymax = 52.237, ymin = 52.227), crs = st_crs(4326))

x <- opq(bbox = bb) %>%
  add_osm_feature(key = c('highway')) %>%
  osmdata_sf()

net <- x$osm_lines %>% 
  filter(highway == "motorway") %>% 
  as_sfnetwork() %>%
  activate("edges") %>%
  mutate(bearing = edge_azimuth()) %>%
  mutate(bearing = units::set_units(bearing, "degrees"))

plot(st_as_sf(net)[, "bearing"])

reprex package (v2.0.1)

You might be interested in using the sfnetworks package for spatial networks. It includes the function edge_azimuth() to calculate the azimuth of the edges in a network.

Do note that the OSM data might need some pre-processing before they form a clean, routable network. See e.g. the sfnetworks vignette on pre-processing and cleaning: https://luukvdmeer.github.io/sfnetworks/articles/sfn02_preprocess_clean.html

Reprex:

library(osmdata)
#> Data (c) OpenStreetMap contributors, ODbL 1.0. https://www.openstreetmap.org/copyright
library(sf)
#> Linking to GEOS 3.10.1, GDAL 3.4.0, PROJ 8.2.0; sf_use_s2() is TRUE
library(dplyr)
library(sfnetworks)

bb <- st_bbox(c(xmin = 5.22, xmax = 5.246, ymax = 52.237, ymin = 52.227), crs = st_crs(4326))

x <- opq(bbox = bb) %>%
  add_osm_feature(key = c('highway')) %>%
  osmdata_sf()

net <- x$osm_lines %>% 
  filter(highway == "motorway") %>% 
  as_sfnetwork() %>%
  activate("edges") %>%
  mutate(bearing = edge_azimuth()) %>%
  mutate(bearing = units::set_units(bearing, "degrees"))

plot(st_as_sf(net)[, "bearing"])

Created on 2022-08-10 by the reprex package (v2.0.1)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文