Мне было интересно, есть ли способ создать linestring из двух точек, указанных в одной строке фрейма данных в новом столбце геометрии. Другими словами, долгота и широта двух точек задаются во фрейме данных, как показано ниже:
df <- data.frame(id = c("a", "b"), lon1 = c(1,2), lat1 = c(3,4), lon2 = c(5,6), lat2 = c(7,8))
где lon1 и lat1 представляют координаты первой точки, а lon2 и lat2 представляют собой координаты второй точки. Желаемый фрейм данных должен иметь две строки и два столбца - столбец id и столбец geometry.
Я пробовал с sf::st_linestring, но, похоже, эта функция работает только с матрицами.
Желаемый фрейм данных:
desired_df <- data.frame(id = c("a", "a", "b", "b"), lon = c(1,2,5,6), lat = c(3,4,7,8)) %>% st_as_sf(coords = c("lon", "lat"), dim = "XY") %>% st_set_crs(4236) %>% group_by(id) %>% summarise(geometry = st_union(geometry), do_union = FALSE) %>% st_cast("LINESTRING")





Мы можем перебрать строки с помощью pmap и применить st_linestring к созданному matrix.
library(tidyverse)
library(sf)
out <- pmap(df[-1], ~
c(...) %>%
matrix(., , ncol=2, byrow = TRUE) %>%
st_linestring) %>%
reduce(st_sfc) %>%
mutate(df, geometry = .)
out$geometry
#Geometry set for 2 features
#geometry type: LINESTRING
#dimension: XY
#bbox: xmin: 1 ymin: 3 xmax: 6 ymax: 8
#epsg (SRID): NA
#proj4string: NA
#LINESTRING (1 3, 5 7)
#LINESTRING (2 4, 6 8)
@adl Можете ли вы проверить, в порядке ли изменение?
в результате все еще остается список. В каждой строке есть список в столбце lstring, а не просто строка
@adl А сейчас
формат в порядке, но содержимое внутри строки неверно. Желаемый фрейм данных должен выглядеть следующим образом: df <- data.frame (id = c ("a", "a", "b", "b"), lon = c (1,2,5,6), lat = c (3,4,7,8))%>% st_as_sf (coords = c ("lon", "lat"), dim = "XY")%>% st_set_crs (4236)%>% group_by (id )%>% summarize (geometry = st_union (geometry), do_union = FALSE)%>% st_cast ("LINESTRING")
@adl Я думаю, что раньше упустил st_sfc.
Я пытаюсь понять магию муррра, которая здесь произошла. Что именно делает c(...)in pmap(df[-1], ~c(...))? Не могли бы вы заменить нотацию формулы ~ анонимной функцией?
Вдобавок reduce(st_sfc) работает только с двумя строками .. если фрейм данных длиннее этого, возвращается ошибка (Error in vapply(lst, class, rep(NA_character_, 3)) : values must be length 3, but FUN(X[[1]]) result is length 2)
@Ratnanil Это просто объединение c. Вы можете использовать анонимный вызов функции, но отчасти люди используют эти функции, чтобы сделать их чистыми, компактными и т. д. (Это тоже субъективно). По поводу ошибки, возможно, вам придется проверить поведение st_sfc
Проблема с моим исходным ответом заключается в том, что он неправильно устанавливает ограничивающую рамку.
Сегодня я бы использовал этот подход, используя sfheaders и data.table.
library(data.table)
library(sfheaders)
dt <- as.data.table(df)
## To use `sfheaders` the data needs to be in long form
dt1 <- dt[, .(id, lon = lon1, lat = lat1)]
dt2 <- dt[, .(id, lon = lon2, lat = lat2)]
## Add on a 'sequence' variable so we know which one comes first
dt1[, seq := 1L ]
dt2[, seq := 2L ]
## put back together
dt <- rbindlist(list(dt1, dt2), use.names = TRUE)
setorder(dt, id, seq)
sf <- sfheaders::sf_linestring(
obj = dt
, x = "lon"
, y = "lat"
, linestring_id = "id"
)
sf
# Simple feature collection with 2 features and 1 field
# geometry type: LINESTRING
# dimension: XY
# bbox: xmin: 1 ymin: 3 xmax: 6 ymax: 8
# CRS: NA
# id geometry
# 1 a LINESTRING (1 3, 5 7)
# 2 b LINESTRING (2 4, 6 8)
Альтернативный подход с использованием data.table
требовать (data.table)
dt <- as.data.table(df)
sf <- dt[
, {
geometry <- sf::st_linestring(x = matrix(c(lon1, lon2, lat1, lat2), nrow = 2, ncol = 2))
geometry <- sf::st_sfc(geometry)
geometry <- sf::st_sf(geometry = geometry)
}
, by = id
]
sf::st_as_sf(sf)
# Simple feature collection with 2 features and 1 field
# geometry type: LINESTRING
# dimension: XY
# bbox: xmin: 1 ymin: 3 xmax: 5 ymax: 7
# epsg (SRID): NA
# proj4string: NA
# id geometry
# 1 a LINESTRING (1 3, 5 7)
# 2 b LINESTRING (2 4, 6 8)
Это решение также использует purrrpmap, получая результат в желаемом формате.
library(tidyverse)
library(sf)
df <- data.frame(id = c("a", "b"), lon1 = c(1,2), lat1 = c(3,4), lon2 = c(5,6), lat2 = c(7,8))
make_line <- function(lon1, lat1, lon2, lat2) {
st_linestring(matrix(c(lon1, lon2, lat1, lat2), 2, 2))
}
df %>%
select(-id) %>%
pmap(make_line) %>%
st_as_sfc(crs = 4326) %>%
{tibble(id = df$id, geometry = .)} %>%
st_sf()
Результат:
Simple feature collection with 2 features and 1 field
geometry type: LINESTRING
dimension: XY
bbox: xmin: 1 ymin: 3 xmax: 6 ymax: 8
epsg (SRID): 4326
proj4string: +proj=longlat +datum=WGS84 +no_defs
# A tibble: 2 x 2
id geometry
<fct> <LINESTRING [°]>
1 a (1 3, 5 7)
2 b (2 4, 6 8)
df = data.frame(id = c("a", "b"), lon1 = c(1,2), lat1 = c(3,4), lon2 = c(5,6), lat2 = c(7,8))
df
## id lon1 lat1 lon2 lat2
## 1 a 1 3 5 7
## 2 b 2 4 6 8
Вот еще один способ прохождения WKT:
library(sf)
df$geom = sprintf("LINESTRING(%s %s, %s %s)", df$lon1, df$lat1, df$lon2, df$lat2)
df = st_as_sf(df, wkt = "geom")
df
## Simple feature collection with 2 features and 5 fields
## geometry type: LINESTRING
## dimension: XY
## bbox: xmin: 1 ymin: 3 xmax: 6 ymax: 8
## CRS: NA
## id lon1 lat1 lon2 lat2 geom
## 1 a 1 3 5 7 LINESTRING (1 3, 5 7)
## 2 b 2 4 6 8 LINESTRING (2 4, 6 8)
Есть ли возможность, чтобы столбец строки был столбцом геометрии, а не списком?