Измерение расстояния всех точек вдоль линии в R (линейная строка в sf) - PullRequest
1 голос
/ 28 апреля 2019

У меня есть сглаженная линия (упрощенная абстракция береговой линии), которая равна linestring, и я хочу измерять длину линии через определенные промежутки времени вдоль нее. Я могу создать сглаженную линию и измерить ее длину:

library(raster)
library(sf)
library(tidyverse)
library(rnaturalearth)
library(smoothr)

# create bounding box for the line 
xmin=-80
xmax=-66
ymin=24
ymax=45
bbox <- extent(xmin, xmax, ymin, ymax)

# get coarse outline of the US 
usamap <- rnaturalearth::ne_countries(scale = "small", country = "united states of america", returnclass = "sf")[1] %>% 
  st_cast("MULTILINESTRING")

# crop to extent of the bbox and get rid of a border line that isn't coastal 
bbox2 <- st_set_crs(st_as_sf(as(raster::extent(-80, -74, 42, 45.5), "SpatialPolygons")), st_crs(usamap))
cropmap <- usamap %>% 
  st_crop(bbox) %>% 
  st_difference(bbox2)

# smooth the line 
smoothmap <- cropmap %>% 
  smoothr::smooth(method="ksmooth", smoothness=8)

# measure the line length
st_length(smoothmap) # I get 1855956m

Я собираюсь «привязать» образцы участков к точкам вдоль этой линии, и мне нужно знать, как далеко они находятся вдоль береговой линии. Однако я не могу понять, как измерить длину линии с интервалами вдоль береговой линии. Размер интервала не очень важен, если он имеет относительно хорошее разрешение, возможно, каждые 1 км или каждые 0,01 градуса.

То, что я хотел бы создать, - это кадр данных со столбцами x, y, содержащими широту / долготу точек вдоль линии, и столбец «length», содержащий расстояние вдоль линии (от начала координат до этой точки). Вот некоторые из вещей, которые я пробовал:

Итерация по ограничивающим прямоугольникам. Я попытался обрезать линию с помощью ограничивающих прямоугольников меньшего и меньшего размера (используя регулярные интервалы в широте / долготе), но поскольку линия изгибается "назад" вокруг -76, 38, некоторые из обрезанных полей не охватывают весь отрезок, который я ожидал. Этот подход работает для верхней правой половины строки, но не для нижней левой половины, которая просто возвращает нулевую длину.

Обрезка экстента перед внедрением сглаживателя, а затем измерение линии. Поскольку функция сглаживания не дает такую ​​же форму, если она измеряет только сегмент исходной линии, на самом деле это не так измерить расстояние по той же линии.

Получение координат linestring с помощью st_coordinates, обрезка одной строки (одной точки на линии) и восстановление оставшихся координат в виде linestring. Этот подход не дает один linestring, но вместо этого цепочка точек (поскольку st_cast не знает, как их снова соединить), поэтому его нельзя измерить как обычно.

Было бы идеально "отредактировать" геометрию smoothmap, чтобы удалить одну строку координат за раз, повторно измерить линию и записать координаты конечной точки и длину линии в кадр данных. Однако я не уверен, возможно ли редактировать координаты sf-объекта, не превращая его в кадр данных.

Ответы [ 2 ]

1 голос
/ 29 апреля 2019

Если я понимаю ваш вопрос, я думаю, вы можете сделать это:

x <- as(smoothmap, "Spatial")
g <- geom(x)
d <- pointDistance(g[-nrow(g), c("x", "y")], g[-1, c("x", "y")], lonlat=TRUE)
gg <- data.frame(g[, c('x','y')], seglength=c(d, 0))

gg$lengthfromhere <- rev(cumsum(rev(gg[,"seglength"])))

head(gg)

#          x        y seglength lengthfromhere
#1 -67.06494 45.00000 70850.765        1855956
#2 -67.74832 44.58805  2405.180        1785105
#3 -67.77221 44.57474  2490.175        1782700
#4 -67.79692 44.56095  2577.254        1780210
#5 -67.82248 44.54667  2666.340        1777633
#6 -67.84890 44.53189  2757.336        1774967

tail(gg)
#            x        y  seglength lengthfromhere
#539 -79.09383 33.34224   2580.481       111543.5
#540 -79.11531 33.32753   2542.557       108963.0
#541 -79.13648 33.31306   2512.564       106420.5
#542 -79.15739 33.29874   2479.949       103907.9
#543 -79.17802 33.28460 101427.939       101427.9
#544 -80.00000 32.68751      0.000            0.0
0 голосов
/ 21 июля 2019

Я считаю, что вам нужно sf::st_line_sample:

# Transform to metric
smoothmap_utm <- st_transform(smoothmap, 3857)

# Get samples at every kilometer
smoothmap_samples <- st_line_sample(smoothmap_utm, density = 1/1000)

# Transform back to a sf data.frame
smoothmaps_points <- map(smoothmap_samples, function(x) data.frame(geometry = st_geometry(x))) %>%
  map_df(as.data.frame) %>% st_sf() %>%
  st_cast("POINT") %>%
  st_set_crs(3857) %>%
  st_transform(4326)

mapview(smoothmaps_points) + mapview(smoothmap)

snap

Как добраться до желаемого результата:

# Function to transform sf to lon,lat

sfc_as_cols <- function(x, names = c("lon","lat")) {
  stopifnot(inherits(x,"sf") && inherits(sf::st_geometry(x),"sfc_POINT"))
  ret <- sf::st_coordinates(x)
  ret <- tibble::as_tibble(ret)
  stopifnot(length(names) == ncol(ret))
  x <- x[ , !names(x) %in% names]
  ret <- setNames(ret,names)
  ui <- dplyr::bind_cols(x,ret)
  st_set_geometry(ui, NULL)
}

smoothmaps_points_xy <- sfc_as_cols(smoothmaps_points) %>%
  mutate(dist = cumsum(c(0, rep(1000, times = n() - 1))))

smoothmaps_points_xy
        lon      lat dist
1 -67.06836 44.99794    0
2 -67.07521 44.99383 1000
3 -67.08206 44.98972 2000
4 -67.08891 44.98560 3000
5 -67.09575 44.98149 4000
6 -67.10260 44.97737 5000

Важно

Но если ваша конечная цель состоит в том, чтобы получить расстояние в точке на пути, я бы рекомендовал проверить rgeos::gProject.

...