Замена определенной геометрии sf-точки не удалась
При попытке заменить набор точек в столбце sf sfc, я получаю несколько видов ошибок. Я пробовал несколько вещей, используя как dplyr, так и базовые методы, каждый из которых приводил к ошибкам. Единственное решение, которое я нашел до сих пор, - это полная замена столбца геометрии sfc, что кажется неоптимальным решением.
В моем конкретном примере у меня есть четыре точки, две из которых имеют одинаковый идентификатор в столбце "а". Я пытаюсь обновить геометрию для точек с идентификатором 17 от (11,12) до (12,13). В качестве иллюстрации я также пытаюсь обновить геометрию для ID 16, обновив ее с (0,1) до (5,6).
library(tidyverse)
library(sf)
#> Linking to GEOS 3.6.1, GDAL 2.2.3, PROJ 4.9.3
geom = st_sfc(st_point(c(0,1)), st_point(c(0,1)), st_point(c(11,12)),st_point(c(11,12)))
a_col = c(15,16,17,17)
sf = st_sf(a = a_col, geometry = geom)
#Approach 1. doesn't work for a== 17
sf2 <-
sf %>%
mutate(geometry = case_when(a==17 ~ st_sfc(st_point(c(12,13))),
TRUE ~ geometry))
#> Error in mutate_impl(.data, dots): Evaluation error: must be sfc_GEOMETRY/sfc, not sfc_POINT/sfc.
#Approach 1 also doesn't work for a==16
sf2 <-
sf %>%
mutate(geometry = case_when(a==16 ~ st_sfc(st_point(c(5,6))),
TRUE ~ geometry))
#> Error in mutate_impl(.data, dots): Evaluation error: must be sfc_GEOMETRY/sfc, not sfc_POINT/sfc.
#Approach 2. This works when column a value is unique...
sf2 <- sf
st_geometry(sf2[sf2$a ==16,]) <- st_sfc(st_point(c(5,6)))
#but fails when column a value is not unique.
sf2 <- sf
st_geometry(sf2[sf2$a ==17,]) <- st_sfc(st_point(c(12,13)))
#> Error in `st_geometry<-.sf`(`*tmp*`, value = structure(list(structure(c(12, : nrow(x) == length(value) is not TRUE
#Approach 3. could wholesale replace the geometry sfc column, but seems like there's a better way
geom2 <- geom
geom2[sf$a==17] <- st_point(c(12,13))
sf2 = st_sf(a = a_col, geometry = geom2)
Как вы можете видеть, Подход 1 (dplyr::case_when и dplyr::mutate) терпит неудачу - это был бы мой предпочтительный подход, так как я работаю в стиле tidyverse и у меня есть другие условия и логика для применения в этой конкретной задаче. Подход 2 (замена в st_geometry) работает, когда идентификатор является уникальным, но не работает, когда его нет. Подход 3 работает, но из-за времени обработки, читабельности кода и других причин не будет предпочтительным (примечание: получено из этого вопроса SO Замените геометрии из списка в sf)
Любые рекомендуемые решения, особенно в соответствии с подходом 1 - dplyr::case_when?
2 ответа
Проблема в том, что case_when
требует, чтобы все результаты имели одинаковый класс. Таким образом, сообщение об ошибке говорит вам, что результат st_sfc(st_point(c(5,6)))
а также geometry
не тот же класс. Если вы используете ifelse
вместо этого вы можете просто избежать требования, чтобы они были одного и того же класса.
sf2 <-
sf %>%
mutate(geometry = ifelse(a==16, st_sfc(st_point(c(5,6))), geometry))
Вы также можете настроить классы двух результатов, используя st_cast
sf2 <-
sf %>%
mutate(geometry = case_when(a==16 ~ st_cast(st_sfc(st_point(c(5,6))), "GEOMETRY"),
TRUE ~ st_cast(geometry, "GEOMETRY")))
Если вы хотите сделать это, не загружая полностью тидиверс или даже его фрагменты, которые вам действительно нужны (пожалуйста, не используйте library(tidyverse)
просто получите биты, которые вам действительно нужны в будущем), тогда этот вариант вашей последней попытки сработает:
> st_geometry(sf2)[sf2$a==17] = st_point(c(12,13))
> sf2
Simple feature collection with 4 features and 1 field
geometry type: POINT
dimension: XY
bbox: xmin: 0 ymin: 1 xmax: 11 ymax: 12
epsg (SRID): NA
proj4string: NA
a geometry
1 15 POINT (0 1)
2 16 POINT (0 1)
3 17 POINT (12 13)
4 17 POINT (12 13)
Вы также можете думать об этом как о вариации вашего третьего примера. st_sfc
является столбцом, и вы пытаетесь заменить столбец столбцом, который имеет меньшее количество значений, поэтому он не работает. Мой пример здесь заменяет значения в столбце, и одно значение на RHS повторяется, чтобы заполнить выбранные элементы в геометрии объекта.
Это, пожалуй, самый быстрый, аккуратный и самый современный способ сделать это.