Не удается заменить определенную геометрию точки sf

При попытке заменить набор точек в столбце sf sfc я получаю несколько видов ошибок. Я пробовал несколько вещей, используя методы dplyr и base, каждый из которых приводил к ошибкам. Единственное решение, которое я нашел до сих пор, - это полностью заменить столбец геометрии 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?


person waiguoren    schedule 09.01.2019    source источник


Ответы (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")))
person see24    schedule 09.01.2019
comment
Это отлично сработало! Думаю, я понимаю это как st_point, создающую простую функцию, а st_sfc превращает ее в тип простого столбца функций (sfc). Есть ли у вас какие-либо указания на то, почему необходим следующий шаг st_cast (., GEOMETRY) (кроме того, что он работает)? Если другие геометрические формы также являются точками, в этом нет необходимости. Не удалось найти много помощи в документации или где-либо еще по этому поводу. - person waiguoren; 09.01.2019
comment
Существуют разные типы sfc (точечные, многоточечные, многоугольные и т. Д.), Поэтому они должны быть такими же, как и другие. Тогда я подумал, что кастинг только одного из результатов сработает, но не знал, почему именно - person see24; 09.01.2019

Если вы хотите сделать это без загрузки всей тидивселенной или даже тех ее частей, которые вам действительно нужны (пожалуйста, не используйте 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 - это столбец, и вы пытаетесь заменить столбец столбцом с меньшим количеством значений, поэтому это не удается. В моем примере здесь заменяются значения в столбце, а одно значение в правой части экрана повторяется для заполнения выбранных элементов в геометрии объекта.

Это, вероятно, самый быстрый, аккуратный и самый рискованный способ сделать это.

person Spacedman    schedule 10.01.2019