Заполнение растровых отверстий в R или Grass GIS

Образец данных

x <- raster(x=matrix(data=1:36, nrow=6), xmn=-1000, xmx=1000, ymn=-100, ymx=900)
x[c(8, 15, 16, 17, 22, 25, 26, 30, 31)] <- NA
plot(x)

введите здесь описание изображения

Проблема

Как отличить (алгоритмически) дыры в растре, т. е. область, ограниченную ячейками c(15:17, 22), от других пробелов, которые не являются дырами (т. е. остальные пустые ячейки)?

Это позволит выполнять операции только с областями отверстий/островов растра, заполнять отверстия пользовательским значением и т.д. и т.п.

Фактические растры имеют около 30000 отверстий, поэтому важна скорость. Меня интересуют ГИС-решения R и Grass. Большое спасибо за вашу помощь, очень признателен!


person shekeine    schedule 16.12.2016    source источник
comment
Я обычно выполняю такие задачи с помощью той же команды, что и вы: x[c(15, 16, 17, 22)] <- Value Однако я также заинтересован в более быстрых решениях.   -  person maRtin    schedule 17.12.2016
comment
Конечно, именно так были созданы дыры в первую очередь; просто для демонстрационных целей. Хитрость заключается в том, чтобы алгоритмически (а не вручную) вывести, что ячейки 15-17,22 являются дырами, а другие пустые ячейки - нет ;-).   -  person shekeine    schedule 17.12.2016


Ответы (3)


А полигонизация? Для скорости я не знаю, сколько это будет стоить, но вы могли бы:

x[!is.na(values(x))]<-1
plot(x)
x[is.na(values(x))]<-0

hole <- rasterToPolygons(x, fun=NULL, n=4, na.rm=TRUE, digits=12, dissolve=T)

Теперь вам нужно преобразовать полигоны из одной части в составную:

hole2 <- SpatialPolygons(lapply(hole@polygons[[1]]@Polygons, function(xx) Polygons(list(xx),ID=round(runif(1,1,100000000)))))

plot(hole2, add=T)

Теперь вы найдете «настоящие» дыры, которые не касаются границы.

around <- as(extent(x), "SpatialLines")
touch_border <- gIntersects(around, hole2, byid=T) 
extract(x, hole2[!touch_border,],cellnumbers=T)

Это дает вам количество ячеек по отверстию. Он нашел ячейку «8», о которой вы не говорите, что это дыра, поэтому я не уверен, что это правильно, но она должна быть очень близко!

Если он медленный в R, сделайте тот же алгоритм в GRASS (в основном вызов rasterToPolygons)

person Bastien    schedule 22.12.2016
comment
Большое спасибо за попытку. Однако ячейки 25, 26, 30, 31 (включенные в многоугольник с именем hole ) не являются дырами: см. здесь объяснение [gis.stackexchange.com /questions/27255/. - person shekeine; 23.12.2016
comment
можете ли вы настроить воспроизводимый пример, чтобы представить эту проблему. Это может помочь найти решение. - person Bastien; 23.12.2016
comment
Вопрос задает именно это, и пример соответственно содержит как дыры, так и открытые области NA/NULL. Прочтите: Как отличить (алгоритмически) дыры в растре, т. е. область, ограниченную ячейками c(15:17, 22), от других пробелов, которые не являются дырами (т. е. остальные пустые ячейки)? Однако большое спасибо за попытку. - person shekeine; 23.12.2016
comment
Думаю, теперь я понимаю. У меня нет ответа (пока), но, если я правильно понимаю, вам нужно преобразовать многоугольник hole[1,] из составного в одиночный. Это даст вам 4 полигона. Затем вы пересекаете эти 4 полигона с границей вашего растра (as(extent(x), "SpatialPolygons")) и оставляете только те, которые не соприкасаются. Тогда у вас есть свои дыры. - person Bastien; 23.12.2016
comment
@SheKeine, изменил это, чтобы приблизиться к вашему последнему комментарию. - person Bastien; 23.12.2016
comment
@bastian Ну, это достаточно быстро с помощью Grass, например, я использую r.to.vect Grass вместо rasterToPolygons R. Большое спасибо! - person shekeine; 28.12.2016

Вот решение без полигонизации: (это не элегантно, но работает). Однако вы должны переклассифицировать свои лунки/островки по значениям (например, 999), а все остальные, не относящиеся к островам, — к NA. Как это:

x <- raster(x=matrix(rep(NA,36), nrow=6), xmn=-1000, xmx=1000, ymn=-100, ymx=900)
x[c(8, 15, 16, 17, 22, 25, 26, 30, 31)] <- 999

plot(x)

Исходный фиктивный растр

Теперь я использую функцию clump(), чтобы проверить, есть ли остров, и самое интересное в этой функции то, что она также возвращает идентификаторы этих островов:

#Get Islands with IDs
cl <- clump(x,directions=8)
plot(cl)

clumps/остров с идентификаторами

Затем я создаю кадр данных из частот островов (это просто для получения идентификатора каждого острова).

freqCl <- as.data.frame(freq(cl))

#remove the (row) which corresponds to the NA values (this is important for the last step)
freqCl <- freqCl[-which(is.na(freqCl$value)),]

Проверьте, не касается ли какой-либо остров границы:

#Check if the island touches any border and therefore isn't a "real island" (first and last column or row)

noIslandID <- c()
#First row
if(any(rownames(freqCl) %in% cl[1,])){
  eliminate   <- rownames(freqCl)[rownames(freqCl) %in% cl[1,]]
  noIslandID  <- append(noIslandID, eliminate)
}
#Last row
if(any(rownames(freqCl) %in% cl[nrow(cl),])){
  eliminate  <- rownames(freqCl)[rownames(freqCl) %in% cl[nrow(cl),]]
  noIslandID <- append(noIslandID, eliminate)
}
#First col
if(any(rownames(freqCl) %in% cl[,1])){
  eliminate   <- rownames(freqCl)[rownames(freqCl) %in% cl[,1]]
  noIslandID  <- append(noIslandID, eliminate)
}
#Last col
if(any(rownames(freqCl) %in% cl[,ncol(cl)])){
  eliminate   <- rownames(freqCl)[rownames(freqCl) %in% cl[,ncol(cl)]]
  noIslandID  <- append(noIslandID, eliminate)
}

Устраните те острова, которые касаются границы:

noIslandID <- unique(noIslandID)
IslandID   <- setdiff(rownames(freqCl), noIslandID)

На последнем шаге назначьте 1 каждому «реальному острову» из исходного растра:

for(i in 1:length(IslandID)) {
  x[cl[]==as.numeric(IslandID[i])] <- 1
}

plot(x)

введите здесь описание изображения

person maRtin    schedule 23.12.2016

В этом случае ячейка находится в дыре, если она ограничена хотя бы одним непропущенным значением в каждом из четырех сторон света. Вы можете проверить это, используя приведенный ниже код, который также заменяет отсутствующие значения на 99.

x2 <- x                        ## Create a copy of the raster
notNA <- !is.na(as.matrix(x))  ## Matrix identifying cells that are not NA

nr <- nrow(x) ## Number of rows
nc <- ncol(x) ## Number of columns

for(i in 2:(nr-1)) {       ## Loop over rows, ignoring first and last
    for(j in 2:(nc-1)) {   ## Loop over columns, ignoring first and last
        if(notNA[i,j]) ## Skip cell if not NA
            next
        ## For NA cells, determine if non-NA cell exists in each cardinal direction
        any.north <- any(notNA[1:(i-1),j])
        any.south <- any(notNA[(i+1):nr,j])
        any.west <- any(notNA[i,1:(j-1)])
        any.east <- any(notNA[i,(j+1):nc])
        if(any.north & any.south & any.west & any.east)
            x2[i,j] <- 99 ## If cell is in hole, repalce with new value
    }
}

Вот новый растр:

Новый растр с пробелами, заполненными значением 99

person Richard    schedule 28.12.2019