Замена определенной геометрии sf-точки не удалась - PullRequest
0 голосов
/ 09 января 2019

При попытке заменить набор точек в столбце 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 ]

0 голосов
/ 10 января 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 - это столбец , и вы пытаетесь заменить столбец на столбец с меньшим количеством значений, поэтому он не работает. Мой пример здесь заменяет значения в столбце , и одно значение в RHS повторяется, чтобы заполнить выбранные элементы в геометрии объекта.

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

0 голосов
/ 09 января 2019

Проблема в том, что 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")))
...