Como funciona o polígono espacial% sobre% polígono ao agregar valores em r?

12

Estou trabalhando em um projeto de epidemiologia ambiental em que tenho exposições pontuais (~ 2.000 operações industriais de suínos - IHOs). Essas IHOs pulverizam em campos próximos, mas as gotas de água e o cheiro das fezes podem viajar quilômetros. Portanto, essas exposições pontuais recebem buffers de 3mi, e quero saber o número de exposições da IHO (de vários tipos - soma da quantidade de esterco, número de porcos, o que for; mais simples, apenas o número de buffers de exposição sobrepostos) por blocos censitários da NC (~ 200.000). Os blocos censitários de exclusão (azul) são (1) qualquer coisa nas 5 principais cidades mais populosas e (2) municípios que não fazem fronteira com um condado com uma IHO (nota: isso foi feito com a função gRelate e os códigos DE-9IM - muito liso!). Veja a imagem abaixo para um visual

insira a descrição da imagem aqui

O último passo é agregar a representação da exposição em buffer a cada bloco censitário. Aqui é onde eu estou perplexo.

Eu tive bons momentos com as funções% over% no pacote sp até agora, mas entendo pela vinheta over que poly-poly e poly-line over são implementados em rgeos. A vinheta cobre apenas poli-linha e poli-auto-referência, e não com agregação, por isso estou um pouco confuso sobre quais são minhas opções para poli-poli com agregação de funções, como soma ou média.

Para um caso de teste, considere o trecho abaixo, um tanto detalhado, que trabalha com o arquivo de fronteiras do país. Isso deve ser copiado e executado como está, pois estou usando uma semente aleatória para os pontos e estou baixando e descompactando o arquivo mundial no código.

Primeiro, criamos 100 pontos e depois usamos a função over com o argumento fn para adicionar o elemento no quadro de dados. Há muitos pontos aqui, mas dê uma olhada na Austrália: 3 pontos, número 3 como etiqueta. Por enquanto, tudo bem.

insira a descrição da imagem aqui

Agora transformamos geometrias para criar buffers, transformar de volta e mapear esses buffers. (Incluído no mapa anterior, já que estou limitado a dois links.) Queremos saber quantos buffers se sobrepõem a cada país - no caso da Austrália, a olho nu, é 4. Não posso, pela vida toda, descobrir o que está acontecendo embora para conseguir isso com a função over. Veja minha bagunça de uma tentativa nas linhas finais do código.

EDIT: Observe que um comentarista no r-sis-geo mencionou a função agregada - também referenciada na questão 63577 de troca de pilhas -, portanto, uma solução alternativa / fluxo pode ser através dessa função, mas não entendo por que precisaria ir agregar polypoly quando over parece ter essa funcionalidade para outros objetos espaciais.

require(maptools)
require(sp)
require(rgdal)
require(rgeos)

download.file("http://thematicmapping.org/downloads/TM_WORLD_BORDERS_SIMPL-0.3.zip", destfile="world.zip")
unzip("world.zip")
world.map = readOGR(dsn=".", "TM_WORLD_BORDERS_SIMPL-0.3", stringsAsFactors = F)
orig.world.map = world.map #hold the object, since I'm going to mess with it.

#Let's create 500 random lat/long points with a single value in the data frame: the number 1
set.seed(1)
n=100
lat.v = runif(n, -90, 90)
lon.v = runif(n, -180, 180)
coords.df = data.frame(lon.v, lat.v)
val.v = data.frame(rep(1,n))
names(val.v) = c("val")
names(coords.df) = c("lon", "lat")
points.spdf = SpatialPointsDataFrame(coords=coords.df, proj4string=CRS("+proj=longlat +datum=WGS84"), data=val.v)
points.spdf = spTransform(points.spdf, CRS(proj4string(world.map)))
plot(world.map, main="World map and points") #replot the map
plot(points.spdf, col="red", pch=20, cex=1, add=T) #...and add points.

#Let's use over with the point data
join.df = over(geometry(world.map), points.spdf,  fn=sum)
plot(world.map, main="World with sum of points, 750mi buffers") #Note - happens to be the count of points, but only b/c val=1.
plot(points.spdf, col="red", pch=20, cex=1, add=T) #...and add points.
world.map@data = data.frame(c(world.map@data, join.df))
#world.map@data = data.frame(c(world.map@data, over(world.map, points.spdf, fun="sum")))
invisible(text(getSpPPolygonsLabptSlots(world.map), labels=as.character(world.map$val), cex=1))
#Note I don't love making labels like above, and am open to better ways... plus I think it's deprecated/ing

#Now buffer...
pointbuff.spdf = gBuffer(spTransform(points.spdf, CRS("+init=EPSG:3358")), width=c(750*1609.344), byid=T)
pointbuff.spdf = spTransform(pointbuff.spdf, world.map@proj4string)
plot(pointbuff.spdf, col=NA, border="pink", add=T)



#Now over with the buffer (poly %over% poly).  How do I do this?
world.map = orig.world.map
join.df = data.frame(unname(over(geometry(world.map), pointbuff.spdf, fn=sum, returnList = F)) ) #Seems I need to unname this...?
names(join.df) = c("val")
world.map@data = data.frame(c(world.map@data, join.df)) #If I don't mess with the join.df, world.map's df is a mess..
plot(world.map, main="World map, points, buffers...and a mess of wrong counts") #replot the map
plot(points.spdf, col="red", pch=20, cex=1, add=T) #...and add points.
plot(pointbuff.spdf, col=NA, border="pink", add=T)
invisible(text(getSpPPolygonsLabptSlots(world.map), labels=as.character(world.map$val), cex=1)) 
#^ But if I do strip it of labels, it seems to be misassigning the results?
# Australia should now show 4 instead of 3.  I'm obviously super confused, probably about the structure of over poly-poly returns.  Help?
Mike Dolan Fliss
fonte
Aprecie o redirecionamento - devo excluir daqui e repassar por lá? Qual é a melhor jogada? Obrigado.
Mike Dolan Fliss

Respostas:

5

Obrigado pela pergunta clara e pelo exemplo reproduzível.

Seu entendimento está correto, e isso se resume a um bug no rgeos :: over, que foi corrigido há um mês, mas ainda não foi lançado no CRAN. A seguir, uma solução alternativa, se você estiver interessado apenas no número de interseções:

world.map$val = sapply(over(geometry(world.map), pointbuff.spdf, returnList = TRUE), NROW)

Estou usando NROWaqui em vez de, lengthpara que funcione com os rgeos errados (0,3-8, do CRAN) e com os corrigidos (0,3-10, do r-forge). A sugestão anterior de usar

a = aggregate(pointbuff.spdf, world.map, sum)

também conta o número de interseções, mas apenas com a versão fixa do rgeos instalada. Sua vantagem, além de um nome mais intuitivo, é que ele retorna um Spatialobjeto diretamente , com a geometria de world.map.

Para que o rgeos 0.3-8 funcione, adicione

setMethod("over",
    signature(x = "SpatialPolygons", y = "SpatialPolygonsDataFrame"),
        rgeos:::overGeomGeomDF)

ao seu script, antes de usar over.

Edzer Pebesma
fonte
Muito útil, obrigado. Desejo particularmente comemorar sua oferta de uma solução que funciona antes e depois da correção. Você se importaria em elaborar: (1) Qual é o erro que estou atingindo aqui-rgeos :: over está retornando uma geografia de polígonos espaciais, não um quadro de dados poli espaciais? Algumas funções não retornam quadros de dados ...? (2) Como isso geralmente funciona com agregados e mais? Estou um pouco confuso quanto às diferenças e casos de uso pretendidos. Realmente aprecio sua pesagem, obrigado. E a nota explicativa: alguma sugestão para entender o ciclo de lançamento do CRAN?
Mike Dolan Fliss
Além disso, quanto à pergunta original: preciso contar o número de exposições, mas também preciso somatá-las - coisas como número de porcos em cada exposição. Contar sobreposições é um começo ... mas parece que a solução de que preciso é reunir os mais novos rgeos, sim? Não há maneira de fazer essa agregação funcional (não apenas contando) sem ela?
Mike Dolan Fliss
(1) rgeos :: over para assinatura SpatialPolygons,SpatialPolygonsDataFramedeve retornar a data.frame, mas retorna um vetor de índice idêntico a quando yteria sido SpatialPolygons. sp::aggregatefaz o que você faz com over de uma maneira mais amigável, retornando o Spatialobjeto em vez do data.frame. Os pacotes CRAN são mantidos por voluntários.
Edzer Pebesma
OK, obrigado Edzer. Parece que o agregado depende de rgeos, portanto, para obter essa funcionalidade antes do ciclo de lançamento do CRAN (quando houver), precisarei descobrir como fazer o download dos rgeos mais recentes e resolver isso. Obrigado. E obrigado por todo o seu trabalho no pacote !!
Mike Dolan Fliss
Edzer, muito obrigado pela nota sobre R-sis-geo. Não tinha certeza de onde estava o melhor lugar para postar, então estou feliz que o tópico agora aponte aqui.
Mike Dolan Fliss
1

Enquanto isso, criei um substituto rápido (e mal codificado) que cria o quadro de dados de que preciso, pois minha pergunta não é bem respondida pela solução somente de contagem acima ou "resolva os novos rgeos", que eu não sou suficientemente habilidoso para entender como fazer.

Esta função é claramente (1) incompleta (observe como eu ignoro o argumento fn) e (2) ineficiente, já que eu a encontro sem as poderosas manipulações de matriz do R / sapply ... (claramente eu estou vindo de outras linguagens sem esse poder), mas sinceramente, ainda estou confuso com o que a estrutura da função over retorna (lista de listas ...? E listas em branco se NA?). Pelo que vale a pena (edições bem-vindas), esta função faz o trabalho que eu preciso realizar com êxito e imita a ação das outras funções excedentes.

Edições bem-vindas:

overhelper <- function(pol, pol.df, fn=sum, verbose=F){
   if(verbose) {cat("Building over geometry...\n"); t=Sys.time(); t}
   geolist = over(geometry(pol), pol.df, returnList = T)
   if(verbose) {cat("Geometry done. Aggregating df. \n"); Sys.time()-t;t=Sys.time();t;}
   results = data.frame(matrix(0,nrow=length(pol), ncol=ncol(pol.df)))
   names(results) = names(pol.df)
   end = length(geolist)

   for (i in 1:end){
     if(verbose) cat(i, "...")
     results[i,] = sapply(pol.df@data[unlist(geolist[i]),], fn)
   }
   if(verbose) cat("Aggregation done! (", Sys.time()-t, ") \n Returning result vector.")
   return (results)
}
Mike Dolan Fliss
fonte
1
Adicionei uma alternativa para corrigir o rgeos 0.3-8, na minha resposta.
Edzer Pebesma